はじめに
APIを呼び出す機能をテストする際に、とりあえずモックサーバーを立てたい。
そんなときに便利なのが、SwaggerのNode.js 製のモジュールであるswagger-nodeです。
SwaggerでAPIを定義しておくだけで、モックサーバーを起動できます。
便利なのは、自前でテストデータを用意しなくても、データ型(string, number, boolean, array, object etc)に応じた適当な値をレスポンスしてくれる点です。
Swaggerの概要やswagger-nodeの使い方はこちらで紹介されているため、詳細は割愛します。
Swaggerとswagger-node
この記事では、Node.jsのインストールが面倒なのと、環境を汚さずに使いたいということで、
swagger-nodeとDockerを組み合わせてモックサーバーを構築する方法を説明します。
前提
dockerとdocker-composeを利用できること
モックサーバーを構築する
Dockerfileの作成
まずはswagger-nodeを内包するDockerイメージを作成します。
最新のLTS版であるNode.js v12だとエラーになるようなので、v10を使用します。
https://github.com/swagger-api/swagger-node/issues/586
npm install --save swagger-router
すればOKとの情報もありましたが、未確認です。
適当なディレクトリでDockerfileを作成します。
FROM node:10
# アプリケーションディレクトリを作成する
WORKDIR /usr/src/app
# swagger-nodeをグローバルインストールする
RUN npm install swagger -g
ENTRYPOINT ["/bin/sh", "-c", "while :; do sleep 10; done"]
これをビルトしてDockerイメージを作成します。
$ docker build . -t swagger-node
swaggerプロジェクトの作成
swaggerプロジェクトを作成するために、swagger-nodeをインストールしたコンテナを起動して、swaggerコマンドを実行します。
作成したswaggerプロジェクトはホストにマウントするために、-v $PWD:/usr/src/app
オプションを付けておきます。
$ docker run -d -v $PWD:/usr/src/app --name first-swagger-container swagger-node
$ docker exec -it first-swagger-container swagger project create sample-project
? Framework?
connect
❯ express
hapi
restify
sails
作成されたsample-projectはこのような構成になります。
swagger.yamlがAPI定義ファイルです。
sample-project/
├ api/
│ ├ controllers/
│ ├ helpers/
│ ├ mocks/
│ └ swagger
│ └ swagger.yaml
├ config/
├ node_modules/
├ test/
├ .gitignore
├ app.js
├ package-lock.json
├ package.json
└ README.md
なお、ここで作成したコンテナは不要なので削除します。
$ docker rm --force first-swagger-container
Dockerコンテナ起動
同じディレクトリにdocker-compose.yamlを作成して、swagger-mock
(mockサーバーコンテナ)とswagger-editor
(swagger.yamlを編集するためのコンテナ)を定義します。
version: "3.6"
services:
swagger-mock:
build: .
environment:
- CHOKIDAR_USEPOLLING=true
ports:
- "10010:10010"
volumes:
- .:/usr/src/app
networks:
examples-net:
ipv4_address: 172.16.239.103
# -m: モックモードとして起動するオプション
entrypoint: bash -c "cd sample-project && swagger project start -m"
swagger-editor:
build: .
ports:
- "8000:8000"
volumes:
- .:/usr/src/app
networks:
examples-net:
ipv4_address: 172.16.239.104
# -s: 起動時にブラウザが立ち上がらないようにする
# -p: 指定しない場合ポートがランダムになるため8000で固定する
# --host: ipv4_addressで指定したIPを設定する
entrypoint: bash -c "cd sample-project && swagger project edit -s -p 8000 --host 172.16.239.104"
networks:
examples-net:
name: examples-net
driver: bridge
ipam:
driver: default
config:
- subnet: 172.16.239.0/24
swagger-mock
のポイントとしては、CHOKIDAR_USEPOLLING=true
を設定している点です。
これは、マウントされているファイルが更新された際にコンテナを自動起動するための設定です。
これによって、swagger.yamlが更新された際に即時にモックサーバーに反映されるようになります。
- 補足
- swagger-nodeには、nodemonというモジュールが組み込まれており、ファイル更新を検知したら自動で再起動する仕組み(いわゆるホットリロード)が実装されています。
しかし、Dockerと組み合わせた場合にこれが効かなくなってしまい、Dockerコンテナとしてホットリロードする方法を採用しました。
- swagger-nodeには、nodemonというモジュールが組み込まれており、ファイル更新を検知したら自動で再起動する仕組み(いわゆるホットリロード)が実装されています。
それではDockerコンテナを起動してみます。
$ docker-compose up -d
コンテナが起動するとswagger-mock
がモックサーバーとして利用できる状態になります。
試しにhttp://<コンテナのIPアドレス>:10010
に対して、hello API(初回作成時のサンプルAPI)を呼ぶと、レスポンスを返してくれます。
$ curl http://192.168.99.100:10010/hello?name=Scott
{"message":"Sample text"}
swagger.yamlを更新
目的とするAPIのモックサーバーを起動するには、swagger.yamlを編集する必要があります。
そこでswagger-editor
コンテナを使用します。
ブラウザでhttp://<コンテナのIPアドレス>:8000
にアクセスするとswagger.yamlの編集画面が開きます。
試しに新しいAPIとして次のようにuser API(Get, POST)を定義してみます。
左側の編集パネルに次のように追記します。
swagger: "2.0"
version: "0.0.1"
title: Hello World App
# during dev, should point to your local machine
host: localhost:10010
# basePath prefixes all resource paths
basePath: /
#
schemes:
# tip: remove http to make production-grade
- http
- https
# format of bodies a client can send (Content-Type)
consumes:
- application/json
# format of the responses to the client (Accepts)
produces:
- application/json
paths:
...(略)...
/user:
x-swagger-router-controller: UserController
get:
operationId: getUser
parameters:
- name: id
in: query
required: true
type: string
responses:
"200":
description: Success
schema:
properties:
name:
type: string
age:
type: number
post:
operationId: postUser
parameters:
- name: "user"
in: "body"
required: true
schema:
required:
- name
- age
type: object
properties:
name:
type: string
age:
type: number
responses:
"200":
description: Success
schema:
properties:
message:
type: string
...(略)...
swagger.yamlの書き方について、基本的にはSwaggerの構文に従えばOKです。
ここで重要なのはx-swagger-router-controller
に任意の値を設定することです。
これはswagger-node独自の項目で、ルーティングするコントローラー(.js)を設定するものです。
swagger project start
コマンドで普通に起動する場合は、ここで設定したコントローラー(.js)にルーティングされ、実装された処理に従ってレスポンスが返されます。
モックサーバーと起動する(-m
オプションを付与する)場合は、コントローラーが存在しなくても、responsesで定義している型に応じてレスポンスを返してくれるのです。
動作確認
ブラウザの編集画面に書き込んだ時点で、swagger.yamlが更新され、上述したホットリロードの設定によってswagger-mockコンテナが再起動されます。
つまり、即時にモックサーバーに反映されています。
自身でコンテナを再起動する必要はありません。
実際にuser APIを呼んでみます。
$ curl http://192.168.99.100:10010/user?id=xxx
{"name":"Sample text","age":1}
$ curl -X POST -H 'Content-Type:application/json' -d '{"id": "aaa", "name": "hoge", "age": 10}' http://192.168.99.100:10010/user
{"message":"Sample text"}
適当な値が設定されたレスポンスを返してくれました。
所感
APIを呼び出す機能をテストしたい場合、Swagger定義をもらって編集画面を開いてコピペして、x-swagger-router-controller
という若干の設定を行えば、簡単にモックサーバーを起動できます。
あとは、自分でAPIを定義する場合にも、「swagger.yamlを編集→API呼び出しして確認」というライフサイクルを回しやすそうです。
おわりに
Dockerを勉強中なので、無駄な手順が含まれているように思います。(特にswaggerプロジェクトを作成するあたり)
おいおい勉強して更新していきたいと思います。