大急ぎでAPI＋SPA構成のアプリを立ち上げる（Spring Boot＆Heroku編）

# はじめに

「PoCやるんだけど、こういうの用意できない？
　とりあえず動けばいいから**明日までに**」

![Picture1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/cc2f8de1-9312-fa4d-da12-34c0f559e8d4.png)

# どうする

いやです

## という言葉を飲み込んで

さっさと作っていきましょう。
いろいろ選択肢があるのは存じていますが、当方JavaエンジニアなのでバックエンドはSpring Bootです。
インフラには、忙しいときの心強い味方 **Heroku** を使います。

![Picture2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/bd2035ac-9dc1-56dd-4952-0d7572d7c956.png)

## 環境

**OS**

- macOS Catalina

**ツール・ソフトウェア**

- IntelliJ IDEA 2020.2.4
- Java v1.8.0_111
- Git v2.29.2
- Heroku CLI v7.47.0 (https://devcenter.heroku.com/articles/heroku-cli)
 
## バックエンドから

[Spring Initializr](https://start.spring.io/)で雛形を生成します。
PCの都合でJavaは8。
![Screen Shot 2020-12-01 at 21.10.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/65b2e289-48aa-8d0b-22f4-45fe72705db5.png)

Dependencies（ライブラリ）ですが、Spring WebがあればとりあえずOK。
Lombokは…IDEの設定にひと手間かかるのでやめときます。**急いでるので。**

zipをダウンロード＆解凍し、任意のIDEで開いたら、まずダミーデータを返すAPIを作っていきます。

`com.example`パッケージの下に`controller`と`model`を追加し、以下のクラスを作成します。

![Screen Shot 2020-11-30 at 0.12.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/534be1d6-f383-1e64-10d3-f13d57e42d81.png)

```java
package com.example.demo.model;

// 部署クラス
public class Department {
    public String name;

    public Department(String name) {
        this.name = name;
    }
}
```

```java
package com.example.demo.model;

// 社員クラス
public class Employee {
    public String name;
    public Department department;

    public Employee(String department, String name) {
        this.department = new Department(department);
        this.name = name;
    }
}
```

```java
package com.example.demo.controller;

import com.example.demo.model.Employee;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/api/employees")
public class EmployeeController {

    @GetMapping // 『GET /api/employees』で実行されるようにマッピング
    public List<Employee> getEmployees() {
        List<Employee> results = new ArrayList<>();
        results.add(new Employee("営業", "社員 太郎"));
        results.add(new Employee("開発", "社員 次郎"));
        return results;
    }
}
```

`DemoApplication`を起動します。
IDEの機能でも、コマンドラインから`./gradlew bootRun`でもOKです。

![Screen Shot 2020-11-30 at 1.08.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/48f3b175-af1f-5df1-44db-76be82d8320d.png)
![Screen Shot 2020-11-30 at 1.02.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/05fec024-4aa1-80ac-4af4-8aaf9a61bca2.png)

ブラウザを開き、`http://localhost:8080/api/employees`にアクセス。

![Screen Shot 2020-12-02 at 0.15.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/5a7f4add-4649-eee7-ad86-ec00625b43d3.png)

動いてますね。ヨシ。

## Heroku上で動かす

次は稼働環境を用意します。
[Heroku](https://jp.heroku.com/)上にデプロイしてAPIを公開してみましょう。

リンク先でHerokuの無料アカウントを作成したら、右上のメニューから「Create new app」を選択。

![Screen Shot 2020-12-09 at 19.46.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/c4ba8fb5-ec50-4b64-1960-e62450083a94.png)

好きなアプリ名を入力し、ふたたび「Create new app」をクリック。

![Screen Shot 2020-11-30 at 1.37.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/3b8f2ef5-38da-7b35-15d1-9f96998d1f5b.png)

アプリが作成されると、デフォルトで「Deployment」タブが開いてるかと思います。
デプロイ方法（Deployment method）が３つ並んでいますので、「Heroku Git」を選択。

![Screen Shot 2020-11-30 at 2.03.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/277eed46-aba0-2d5a-2bae-03c1b34445c2.png)

その下に書かれている手順とコマンドに従って、アプリをデプロイします。
ざっくり言うと、gitリポジトリをローカルに作成→herokuをリモートに設定→push という流れです。
Heroku CLIのインストールを忘れずに。

![Screen Shot 2020-12-01 at 2.09.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/5e03d1bb-bd62-204e-f99e-87115c369950.png)

HerokuはgradleとSpring Bootを自動で認識してくれるので、設定ファイルの追加等は不要です。
エラーが出ずに終了したら、「Overview」タグを開いて右側のアクティビティを確認。

![Screen Shot 2020-11-30 at 2.13.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/abdf41fe-5c29-af77-bf98-1a25782d4928.png)

無事にデプロイされたようです。
続いて右上の「Open App」ボタンをクリック。404画面が出てきますが、くじけずURLに「/api/employees」を追加。

![Screen Shot 2020-12-01 at 2.11.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/ec610036-f898-6f55-de3d-5d3ffa9ab1cb.png)
↓
![Screen Shot 2020-11-30 at 2.16.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/ef704339-2821-83ee-354d-0dd72012d859.png)

これでOK。これ以降はpushするたびにアプリが自動で更新されていきます。便利！

## DBを用意する

続いてDBですが、**Herokuに任せます。**
Herokuアドオンの「Heroku Postgres」は、容量は小さいながら無料・速攻でDBが使えるのです。

「Resources」タブの「Add-ons」から検索します。
プランは「Hobby Dev - Free」を選択し、さっそく作成。

![Screen Shot 2020-11-30 at 2.37.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/407ee5e8-05c4-f008-857a-1d26198876f7.png)
![Screen Shot 2020-11-30 at 2.37.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/c0316490-78df-d937-ff12-ca187b3a7615.png)
![Screen Shot 2020-11-30 at 2.38.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/e589ca7f-6702-b24f-5533-8f957c39a2cd.png)

あっという間にご用意されました。

## DBと繋ぐ

先ほど作成したSpring Bootアプリから、DBに接続していきます。
まずはライブラリを追加しましょう。`build.gradle`を開き、`dependencies`に以下を追加します。

```gradle
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.flywaydb:flyway-core'
	runtimeOnly 'org.postgresql:postgresql'
```

ORマッパーには、DBがシンプルだとラクなSpring Data JPAを選択します。
あとはPostgreSQLのドライバと、テーブル作成がラクになるのでFlywayも入れておきます。
（これらはSpring Initializrでも追加できたのですが、設定が必要になるので後回しにしました）

ここからはDBと接続するための設定です。
`src/main/resources/application.properties`に以下の行を追加します。

```properties
spring.datasource.url=spring.datasource.url=${JDBC_DATABASE_URL}
```

Herokuの環境変数には、DB接続用の文字列が`JDBC_DATABASE_URL`として格納されています。
Heroku上で動いているときなら取得できるのですが、ローカルではできません。

ということで、Herokuから接続文字列をもらってきましょう。[Herokuのドキュメント](https://devcenter.heroku.com/articles/connecting-to-relational-databases-on-heroku-with-java)を参考に、以下のコマンドを実行。

```shell
heroku run echo \$JDBC_DATABASE_URL -a (Herokuアプリ名)
```

表示された「jdbc:postgresql://…」という文字列を、ローカルの環境変数に設定します。
IntelliJであれば、`DemoApplication.java`を右クリック→「Edit 'DemoApplication'…」から設定できます。
`application.properties`にベタ書きでも動くのですが、GitHubなどで一般公開しないように注意してください。

![Screen Shot 2020-12-01 at 23.56.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/89a8fc54-f745-25f3-3b94-e72faea31378.png)

続いて、コードをSpring Data JPAの形式に書き換えていきます。
もしここでクラスが見つからないというエラーが出る場合、ライブラリのダウンロードが行われていないかもしれないので、プロジェクトのビルドやIDEの再起動を試してみてください。

`com.example`パッケージの下に`repository``を追加し、以下の**インタフェース**を作成。

```java
package com.example.demo.repository;

import com.example.demo.model.Employee;
import org.springframework.data.repository.CrudRepository;

public interface EmployeeRepository extends CrudRepository<Employee, Integer> {
}
```

`model`パッケージ以下のクラスを修正。

```java
package com.example.demo.model;

import javax.persistence.Entity;
import javax.persistence.Id;

// 部署クラス
@Entity
public class Department {
    @Id
    private Integer id;

    public String name;
}
```

```java
package com.example.demo.model;

import javax.persistence.*;

// 社員クラス
@Entity
public class Employee {
    @Id
    private Integer id;

    public String name;
    
    @ManyToOne
    public Department department;
}
```

`EmployeeController`の`getEmployees`も修正。
Repositoryのメソッドを使い、DBからデータを取得するようにします。

```java
    @GetMapping // 『GET /api/employees』で実行されるようにマッピング
    public Iterable<Employee> getEmployees() {
        return employeeRepository.findAll();
    }
```

最後に、Flyway用のSQLを作成します。
`src/main/resources`の下に`db/migration`フォルダを作成し、中にSQLファイルを入れておくと、
アプリケーション起動時に自動でセットアップしてくれます。
![Screen Shot 2020-12-01 at 23.30.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/8f275e3f-33dd-98d1-a6e3-2595dd365985.png)

```sql
-- V1_0__create_table.sql
CREATE TABLE department(
  id serial PRIMARY KEY,
  name varchar(40) NOT NULL
);

CREATE TABLE employee(
  id serial PRIMARY KEY,
  department_id integer REFERENCES department(id),
  name varchar(40) NOT NULL
);
```

```sql
-- V1_1__insert_data.sql
INSERT INTO department(id, name)
VALUES (1, '営業1課'),
       (2, '開発2課');

-- 自動採番のIDを強引に付与したので、シーケンス管理用の値を再設定
SELECT setval('department_id_seq', MAX(id))
FROM department;


INSERT INTO employee(id, department_id, name)
VALUES (1, 1, '社員 三郎'),
       (2, 2, '社員 四郎');

SELECT setval('employee_id_seq', MAX(id))
FROM employee;
```

では、もう一度アプリケーションを起動します。

![Screen Shot 2020-12-01 at 23.37.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/4eb54166-d0ac-f9b4-0ef0-3cad734dc238.png)

しっかり動いているようです。
これをHerokuにpushすると…

```shell
git add .
git commit -am "db access"
git push heroku master
```

![Screen Shot 2020-12-02 at 0.02.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255659/f0b84dec-bfd5-feb4-8e22-20beecf69e58.png)

無事に反映されました。

## さいごに

とりあえず大急ぎでAPIサーバをセットアップしました。
APIを誰でも実行できたり、ローカルとサーバで同じDBを参照していたりと問題は山積みですが、一旦ここで区切ります。
次回の記事では、フロントエンドも大急ぎで立ち上げていきます。

## 参考リンク
- [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
- [Herokuに Spring Boot アプリケーションをデプロイする](https://devcenter.heroku.com/ja/articles/deploying-spring-boot-apps-to-heroku)