SpringBoot+DockerでAPIを作る方法を０から書いてみる

#はじめに
SpringBootとDockerで簡単なAPIを作成してみました。
全くの０から説明している記事があまり無かったため自分で作って投稿して見ます。ただ正直ApiやDockerについて知識はぜんぜん浅いので間違った事などがあったらご指摘くださいませ！
また、今回はMacで構築することを前提にしていますので御容赦下さい。

#制作環境
・OS:Mac
・DB:MySQL(mariaDB)
・VM:Docker
・言語:SpringBoot(Java8)
・ビルドツール:Maven



#各ツールのインストール
###Dockerのインストール

https://docs.docker.com/docker-for-mac/install/
こちらのURLからダウンロードしてインストールしてください。<img width="1440" alt="Dockerダウンロード方法１.png" src="https://qiita-image-store.s3.amazonaws.com/0/249334/a379a2f5-f875-c22d-c08c-afe20509c80c.png">
<img width="1440" alt="Dockerインストール方法２.png" src="https://qiita-image-store.s3.amazonaws.com/0/249334/8bebc44e-a9af-81e4-a889-df86bed65e7b.png">


###Javaのインストール

こちらからダウンロードしましょう
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

今回は1.8.0-181を使用しています。


###Mavenのインストール

Mavenのバージョンに関しては特に何でもいいのですが、今回は`3.5.2`を使用していきます。
こちらを参考にしてみてください。
[Mavenの特定のバージョンのインストール方法](https://qiita.com/reiji_matsumura/items/98d0ec7c9acd666727f7)

###MySQLのインストール

brewhomeでインストールしていきます。

```
$ brew install mysql
```

###STS(Spring Tool Suite)のインストール
STS(Spring Tool Suite)についてはspringを内包しているEclipseライクなIDEです。
今回はこちらを使ってプロジェクトを作成していきます。
https://spring.io/tools/sts/all
こちらからインストールしましょう。

では必要なものはそろったので制作に入ります。



#制作

User(name, email)を持つEntityをCRUDするAPIを作成します。

作るAPIは以下の通り。

GET /api/users => 全User取得 (getUsers)
GET /api/users/{id} => idのUserを取得 (getUser)
POST /api/users => Userを追加 (createUser)
PUT /api/users/{id} => Userの更新 (updateUser)
DELETE /api/users/{id} => idのUserを削除 (deleteUser)

**STSでプロジェクト作成**
---
file -> new -> Spring Starter Project
でプロジェクトを作っていきます。
今回自分は「`sampleApi`」と言うプロジェクト名で作っていきます。


Spring Initializrでjpa, web, lombok, mysqlを選択してプロジェクトを作成します。

##下準備
Entity,Repository,Serviceを作っていきます。

###Entity

```java
@Data
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String email;
}
```
Entityとは、簡単に言うとDBに登録・更新する値を入れておいたり、DBから取得した値を保持しておく場所です。@Tableでやり取りするデータベースのテーブルを指定しています。こちらのテーブルなどについては後ほど作成していきます。

###Repository

```java

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
```

このRepositoryが、直接BDとのやり取りを行うクラスになります。

###Service

```java

@Service
@Transactional
public class UserService {
    @Autowired
    UserRepository userRepository;

    public User findUser(Long id){
        return userRepository.findOne(id);
    }

    public List<User> findUsers(){
        return userRepository.findAll();
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void delete(Long id) {
        userRepository.delete(id);
    }
}
```

このServiceクラスでビジネスロジック的な処理を行っています。
次に記述しているコントローラから値を受け取ってRepositoryに処理するよう命令したりしていますね。

##Controller
Userをやり取りするためのControllerを作ります。HTTPリクエストに対応する処理を記述します。Userのところをリソース名に置き換えれば他のリソースにも使いまわせそう。Responseコードを今回は指定しなかったが厳密に実装する場合は指定すると良いかも?

```JAVA
@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    UserService userService;

    @RequestMapping(method = RequestMethod.GET)
    public List<User> getUsers() {
        return userService.findUsers();
    }

    @RequestMapping(method=RequestMethod.GET, value="{id}")
    public User getUser(@PathVariable("id") Long id) {
        return userService.findUser(id);
    }

    @RequestMapping(method=RequestMethod.POST)
    public User createUser(@Validated @RequestBody User user) {
        return userService.save(user);
    }

    @RequestMapping(method=RequestMethod.PUT, value="{id}")
    public User updateUser(@PathVariable("id") Long id, @RequestBody User user) {
        user.setId(id);
        return userService.save(user);
    }

    @RequestMapping(method=RequestMethod.DELETE, value="{id}")
    public void deleteUser(@PathVariable("id") Long id) {
        userService.delete(id);
    }

}
```

`@RestController`がRestAPIを作るためのアノテーションで、`@Controller` + `@ResponseBody` + JSONでやり取りができるようになる。spring-boot-starter-webがJacksonで受け取るところと表示するところをうまく処理してくれる。

##Docker
やっときましたdockerですね。
自分もまだ曖昧な知識なのでざっと書いていきます。
まずDockerのコンテナを立ち上げるにはファイル「docker-compose.yml」と「Dockerfile」が必要なのでこの二つのファイルをプロジェクトフォルダの直下に作成します。

作成したらファイルの中身を書いていきます
**まずは「`docker-compose.yml`」**

```yml

version: '3'
services:
  db:
    image: mariadb:10.2
    restart: always
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_USER: hogeuser
      MYSQL_PASSWORD: password
      MYSQL_DATABASE: sampleapi_development
    ports:
      - "3306:3306"
    volumes:
      - ./docker/tmp/mysql:/var/lib/mysql
      - ./docker/mysql/initdb.d:/docker-entrypoint-initdb.d
volumes:
  data:
    driver: local
```
データベースのサーバを立ち上げる記述になります。サーバを立ち上げる際にデータベースなども作成してくれます。それがこちらですね

```yml

MYSQL_ROOT_PASSWORD: password
MYSQL_USER: hogeuser
MYSQL_PASSWORD: password
MYSQL_DATABASE: sampleapi_development
```
上からroot権限の際のパスワード、サーバのユーザー名、パスワード、データベース名となっています。


**次に「`Dockerfile`」**
 
```dockerfile

FROM openjdk:jdk-alpine
VOLUME /tmp
RUN mkdir /app
WORKDIR /app
ENV JAVA_OPTS=""
ENV JAR_TARGET "sampleApi-1.0.0-SNAPSHOT.jar"
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar build/libs/sampleApi-1.0.0-SNAPSHOT.jar" ]

```

`docker-compose.yml`に関してはコピペでも動くと思うんですが、`Dockerfile`については`ENV`と`ENTRYPOINT`の「sampleApi-1.0.0-SNAPSHOT.jar」を少し変える必要があります。これはプロジェクトをビルドした時に生成されるjarファイルをターゲット指定する記述になるんですが、プロジェクトによって生成されるファイル名は異なってきます。以下を参考にして書き直してください。

プロジェクト内に「pom.xml」があると思いますのでその中に

```xml

<artifactId>sampleApi</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
```

このような記載があると思います。みてわかる通りこの「`artifactId`」と「`version`」と「`packeging`」の中身を見て書き直してください。

では次に「`application.properties`」の中身を記入していきます。
こちrのファイルは「src/main/resources」の中にあります。
以下を記入します。

```properties

spring.datasource.url=jdbc:mysql://localhost:3306/sampleapi_development
spring.datasource.username=hogeuser
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
```

こちらはプロジェクトとデータベースを繋げる際に必要なものになりまして、上の「docker-compose.yml」で作成したユーザー名やデータベース名などを記述しています。

これで作成は終わりましたので実際に起動していきます。
まずはコマンドでプロジェクト直下まで移動してください。

```
$ cd ~/sampleApi
```

ではDockerを起動していきます。以下を打ち込んでください。

```
$ docker-compose up --build
```
エラーがでなければ成功です。

ではアプリケーションを立ち上げましょう。と思ったんですがまだデータベースの中身（テーブル）を作っていなかったので作成していきます。
Dockerで立ち上げたデータベースサーバ内に入る必要がありますね。
まずは以下を打ち込んでサーバー名を確認します。

```terminal

$ docker-compose ps
     Name                   Command             State           Ports          
------------------------------------------------------------------------------
sampleapi_db_1   docker-entrypoint.sh mysqld   Up      0.0.0.0:3306->3306/tcp 
```
サーバー名がわかったので入っていきましょう。

```terminal

$ docker exec -it sampleapi_db_1 bash
root@c71a21817893:/# mysql -h localhost -P 3306 -u hogeuser -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 20
Server version: 10.2.16-MariaDB-1:10.2.16+maria~bionic mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>  
```

データベースが作成されているか確認して見ます。

```terminal

MariaDB [(none)]> show databases;
+-----------------------+
| Database              |
+-----------------------+
| information_schema    |
| sampleapi_development |
+-----------------------+
2 rows in set (0.06 sec)

MariaDB [(none)]> 
```

問題ないですね

ではテーブルを作っていきます。

create文はこちら

`CREATE TABLE users (
    id int NOT NULL AUTO_INCREMENT,
    name varchar(255) NOT NULL,
    email varchar(255) NOT NULL,
    PRIMARY KEY (id)
);
`
ただこのままではできないと思いますのでデータベースを切り替える必要があります。
以下のコマンドで切り替えて作成していきましょう。

```terminal

//データベースの切り替え
MariaDB [(none)]> use sampleapi_development;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [sampleapi_development]> CREATE TABLE users (
    ->     id int NOT NULL AUTO_INCREMENT,
    ->     name varchar(255) NOT NULL,
    ->     email varchar(255) NOT NULL,
    ->     PRIMARY KEY (id)
    -> );
Query OK, 0 rows affected (0.02 sec)

MariaDB [sampleapi_development]> INSERT INTO users(id, name, email) VALUES(1, 'ishii', 'ishii@ishii.tech');
Query OK, 1 row affected (0.01 sec)
```

データがあったほうが分かりやすいのでINSERT文で追加しておきました。

##アプリケーションの起動
では本当に中身は作成し終わったのでアプリを起動しましょう。STSを開きプロジェクトを右クリック。
Run as →　Spring Boot App
と起動しましょう。

APIがうまく行っているか確認して見ます。

```
$ curl localhost:8080/api/users
[{"id":1,"name":"ishii","email":"ishii@ishii.tech"}]
```
ちゃんと取得できますね

ユーザを追加してみます

```
$ curl -XPOST -H "Content-Type:application/json" http://localhost:8080/api/users -d '{
  "name":"ishii2","email":"ishii2@ishii.tech"
}'
{"id":2,"name":"ishii2","email":"ishii2@ishii.tech"}
```

問題ないですね！

ちなみにdockerを落とすには

```
$ docker-compose down
```
とすることで落とせます。
遊び終わった後はちゃんとdockerを落とすことをおすすめします。

##最後に
データベースに関しては、本当はマイグレーションを使用したほうがいいとは思います。ただ今回はそこまで自分が追いかけられなかったのと記事が肥大化してしまうとうことで直接データを作るということをしています。この辺りもまた書けたらなと思います。

###参考にしたサイト
・API構築
https://ishiis.net/2016/09/08/spring-boot-rest-api/
・docker
https://qiita.com/ken0909/items/a3f8594ce677bbc7c4c2
・dockerアプリ内のDBへアクセス
https://qiita.com/M_Nagata/items/120831bb4e4a3deace13

---
以上が自分なりに作って見たDocker+SpringでのAPIの作り方でした。
醜い記事ではありますが最後まで見て下さった方有難うございました。
現在railsも勉強中ですので将来そちらの記事も書けたらいいなと思います。