Swaggerを管理しつつ、スタブを自動生成する環境をDockerを使って構築する

APIドキュメントとスタブをまとめて管理できる環境を作りました。

# やりたいこと
フロントエンドとバックエンドの認識合わせ、及び開発のため、APIの定義が存在するファイルはフロントエンドとバックエンドが共有するリポジトリに載せておきたいです。
そこで、APIの定義にはSwaggerを使うとします。
この場合、下記を全て実現できると幸せになれると感じました。

* SwaggerをSwagger Editorを使ってAPI定義を書きたい
* Swagger UIでAPI定義を見たい
* 作ったSwaggerファイルから、自動生成されたレスポンスを返してくれるスタブが欲しい

ということで、これらを実現する環境をDockerを使って構築していきたいと思います。
Dockerを使えば環境構築も一瞬で終わりますし、スタブの再生成もコンテナをビルドするだけで行えるので、それぞれ相性が良いです。

# 構成
* API作成 & 編集: Swagger Editor  
  公式ページ: https://swagger.io/docs/open-source-tools/swagger-editor/
* APIドキュメント閲覧: Swagger UI  
  公式ページ: https://swagger.io/docs/open-source-tools/swagger-ui
* スタブサーバー生成: Swagger Codegen  
  公式ページ: https://swagger.io/docs/open-source-tools/swagger-codegen/
  
# ソース
## ディレクトリ構成
```
.
├── docker/
│   └── stub/
│       └── Dockerfile  # スタブ用Dockerfile
├── swagger/            # swagger関連のソース用ディレクトリ
│   ├── stub/           # swagger.yamlから自動生成されたスタブのソース
│   │   ├── index.js
│   │   ├── ...
│   │   └── ...
│   └── swagger.yaml    # API定義ファイル
└── Makefile            # 各種コマンド管理
```

Swagger Editorを使ってAPI定義ファイルであるswagger.yamlを生成し、そのファイルを元にスタブに設置するファイル及び、スタブコンテナをビルドします。

## Makefile
ビルドコマンドや各種サーバー起動コマンドの定義です。

```Makefile
IMAGE_STUB=api_stub
PORT_SWAGGER_EDITOR=8081
PORT_SWAGGER_UI=8082
PORT_STUB=8080
SWAGGER_DIR_PATH=$(PWD)/swagger
SWAGGER_YAML_PATH=${SWAGGER_DIR_PATH}/swagger.yaml

# swagger.yamlからスタブ用ソース及びスタブコンテナをビルドする
.PHONY: build_stub
build_stub:
	@echo "=== Generating stub file from swagger.yaml is running... ==="
	docker run --rm \
		-v ${SWAGGER_DIR_PATH}:/swagger \
		swaggerapi/swagger-codegen-cli generate \
		-l nodejs-server \
		-i /swagger/swagger.yaml \
		-o /swagger/stub
	@echo "=== Generating stub file is completed. ===\n"
	@echo "=== Building new container... ==="
	docker build -f docker/stub/Dockerfile -t ${IMAGE_STUB} .
	@echo "=== Building container is completed. ==="

# スタブを起動
.PHONY: run_stub
run_stub:
	docker run --rm -p ${PORT_STUB}:8000 ${IMAGE_STUB}

# Swagger Editor用サーバーを起動
.PHONY: run_swagger_editor
run_swagger_editor:
	docker run --rm -p ${PORT_SWAGGER_EDITOR}:8080 swaggerapi/swagger-editor

# Swagger UI用サーバーを起動
.PHONY: run_swagger_ui
run_swagger_ui:
	docker run --rm -p ${PORT_SWAGGER_UI}:8080 \
		-v ${SWAGGER_YAML_PATH}:/usr/share/nginx/html/api/swagger.yaml \
		-e API_URL=http://localhost:${PORT_SWAGGER_UI}/api/swagger.yaml \
		swaggerapi/swagger-ui
```

## docker/stub/Dockerfile
Node.jsでホスティングをするだけのシンプルなサーバーです。

```Dockerfile
FROM node:13.7.0-alpine3.10

ENV APP_HOME /usr/src/app
RUN mkdir -p $APP_HOME
WORKDIR $APP_HOME

# スタブ用ソースをコンテナにコピー
COPY swagger/stub/ $APP_HOME

EXPOSE 8000
RUN npm install

ENTRYPOINT ["npm", "start"]
```

# 使ってみる
## ステップ1: Swagger Editor
まずは、`$ make run_swagger_editor` コマンドでSwagger Editorを起動します。
`http://localhost:8081` にアクセス。
<img width="1407" alt="スクリーンショット 2020-02-16 14.57.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154121/073b2d88-346d-6a3c-2bfe-3465bb5f1e4d.png">

Swagger Editor上で、 `GET /persons` APIを定義。

```swagger.yaml
swagger: '2.0'
info:
  version: 1.0.0
  title: Swagger-sample
  description: |
    #### サンプル
schemes:
  - http
host: localhost:8080
basePath: /api/v1
paths:
  /persons:
    get:
      summary: Get all persons.
      parameters: []
      responses:
        200:
          description: A JSON array of person model
          schema: 
            type: array
            items:
              $ref: '#/definitions/Person'
            example:
              - id: 1
                name: Mike
                age: 23
              - id: 2
                name: Jun
                age: 20
              - id: 3
                name: Jack
                age: 26
                
definitions:
  Person:
    type: object
    properties:
      id:
        type: integer
      name:
        type: string
      age:
        type: integer
```

Swagger Editor上の `Save as YAML` をクリックし、ダウンロードされたswagger.yamlをswaggerディレクトリ配下へ移動。
<img width="638" alt="スクリーンショット 2020-02-16 15.19.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154121/ba5e1973-8e7c-705c-d47c-2da497082573.png">

```
$ cp ~/Download/swagger.yaml ./swagger
``` 


## ステープ2: Swagger UIでAPIを確認
`$ make run_swagger_ui` コマンドで、Swagger UIを起動します。
`http://localhost:8082` にアクセス。
すると、先ほど作成したAPIがSwagger UI上で表示されることを確認できます。
<img width="1381" alt="スクリーンショット 2020-02-16 15.23.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154121/56841a89-d4d4-2cfe-cba9-ce4d39e66572.png">

ここまでで、Swaggerを管理する環境はできました。
それでは、本題のスタブの用意へと移りましょう。

## ステップ3: スタブコンテナビルド
`$ make build_stub` でスタブコンテナをビルド。
`Building container is completed.` が表示されれば完了です。

なお、ここでは下記の順でビルドが行われます。
1. スタブ用ソースを自動生成
2. 自動生成されたソースがパッケージングされたコンテナをビルド

## ステップ4: スタブを使用してAPIを呼び出す
`$ make run_stub` でスタブコンテナを起動。
まず、`http://localhost:8080/docs` へアクセスすると、先ほど定義したAPIがドキュメントとして表示されます。
<img width="1278" alt="スクリーンショット 2020-02-16 15.28.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154121/794d2d44-c14a-2627-0ad3-edf5eba08f98.png">

では、`http://localhost:8080/api/v1/persons` へアクセスしてみましょう。
すると、SwaggerファイルにてExampleで定義したレスポンスが返ってくることが確認できます。
<img width="681" alt="スクリーンショット 2020-02-16 15.29.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154121/5952cb24-3516-60cd-6d65-7facb0ac5adf.png">

これで、Swaggerファイルで定義したレスポンスからスタブを自動生成し、呼び出せることが確認できました！

# まとめ
Swagger Editorでswagger.yamlを作成し、それをSwagger UIで確認、及び定義したAPIのレスポンスを返すスタブに変換し呼び出せる環境を構築しました。
APIを開発するのであればAPIドキュメントは必須ですし、さらにそのAPIを開発中にフロントエンド側で使いたいとなるとスタブの開発は不可欠です。
スタブの自動生成を使用すれば、APIとスタブの二重管理などの手間も省けます。

しばらく使ってみて、また知見があれば記録を残そうと思います。

# 補足
Swagger Editorで編集したファイルをダウンロードしてswaggerディレクトリ配下に設置するところのみ、手動のオペレーションが発生してしまっています。
ここに関して自動化させられるやり方をご存じの方がいらっしゃいましたら、アドバイスをいただけますと嬉しいです m(__)m