Swaggerを利用することでREST APIの仕様をドキュメント化できます。
SwaggerではREST APIの仕様をドキュメントしたファイルをSwagger Specと呼びます。
Swagger UIとはSwagger Specの情報を反映させた静的ページを生成するツールのことを言います。
Swagger UIはSwagger Specの情報を可視化するだけでなく、画面上からREST APIを実行する機能も提供しています。
今回は開発中のAPIをSwagger Specでドキュメント化 → Swagger Specの情報が反映されたSwagger UIの画面からAPIリクエストの検証という一連の作業が行えるDocker環境の構築手順について紹介します。
目次
今回作成するDocker環境について
- docker-compose upだけで環境が準備できる
- 「/swagger-ui」でSwagger UIの画面が表示される
- 「/swagger-ui」以外はAPI用のエンドポイントとする
- Swagger Spec編集後、リロードでSwagger UIに変更内容が反映される
- APIはRuby on RailsのAPIモードで作成する
- DBはMySQLを利用
nginxをリバースプロキシとして利用することでAPIの開発環境とSwagger UIを組み合わせます。
図で表現すると以下のようになります。

今回の利用する各種バージョンは以下の通りです。
- Ruby on Rails: 6.0.3.2
- Ruby: 2.7.1
- MySQL: 8.0.21
- nginx: 1.19.3
API開発環境をDockerに作成する
APIモードで作成する『Rails 6 x MySQL 8』Docker環境構築手順を参考に、RailsのAPIモードを利用してAPI開発環境を作成します。
Dockerfileとdocker-compose.ymlは以下の通りです。
Dockerfile
FROM ruby:2.7.1
# 作業ディレクトリを/rails_api_swaggerに指定
WORKDIR /rails_api_swagger
# ローカルのGemfileをDokcerにコピー
COPY Gemfile* /rails_api_swagger/
# /rails_api_swaggerディレクトリ上でbundle install
RUN bundle install
docker-compose.yml
version: '3'
services:
api: # Ruby on Railsが起動するコンテナ
build: .
ports:
- '3000:3000' # localhostの3000ポートでアクセスできるようにする
volumes:
- .:/rails_api_swagger # アプリケーションファイルの同期
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
db: # MySQLが起動するコンテナ
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql # データの永続化
- ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
command: --default-authentication-plugin=mysql_native_password # 認証方式を8系以前のものにする。
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'rails_api_swagger_development'
volumes:
mysql_data: # データボリュームの登録
RailsのAPIモードでは静的ページを生成する機能は除外されているため、Swagger UIを直接Railsアプリケーションに組み込むことはできませんが、今回の方法を利用すればAPIモードでもSwagger UIを利用できます。
サンプルとなるAPIを作成します。
# コンテナをバックグランドで起動
$ docker-compose up -d
# Eventを操作する機能(モデル、ビュー、コントローラー)を一括作成
$ docker-compose exec api rails g scaffold event title:string
# eventsテーブルを作成
$ docker-compose exec api rails db:migrate
# rails consoleでeventsのレコードを作成
$ docker-compose exec api rails c
> event = Event.new(title: 'サンプルイベント')
> event.save
localhost:3000/events
にアクセスして以下のようなレスポンスが返ってくればOKです。
リバースプロキシの設定を行い、nginx経由でAPIにアクセスできるようにする
nginx経由でRailsアプリケーションにアクセスできるようリバースプロキシの設定を行ます。
default.conf
server {
listen 80;
server_name localhost;
# "/"にアクセスがあったときの処理
location / {
proxy_set_header Host localhost; # アクセス元のホストをlocalhostにする
proxy_pass http://api:3000; # apiコンテナの3000ポートにリクエストを送る
}
}
nginxの設定ファイルは/etc/nginx/nginx.conf
です。
設定ファイルにinclude /etc/nginx/conf.d/*.conf;
という記述があることからも分かるように、設定ファイルでは/etc/nginx/conf.d
配下の.conf
という拡張子の設定も読み込んでいます。
つまり、/etc/nginx/conf.d
配下に今回作成した設定ファイルを配置することで、nginxコンテナをリバースプロキシとして利用できます。
docker-compose.yml
にnginxコンテナを追加します。
docker-compose.yml
services:
api:
(略)
db:
(略)
nginx:
image: nginx:1.19.3
ports:
- '80:80'
command: [nginx-debug, '-g', 'daemon off;']
volumes:
- ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- api
volumes:
mysql_data:
[nginx-debug, '-g', 'daemon off;']
はデバッグモードによる起動方法です。1
なお、swagger-uiのDockerイメージもnginxを利用していますが、nginx.conf
にinclude /etc/nginx/conf.d/*.conf;
の記述がありません。
ですので、swagger-uiのDockerイメージを利用する場合だと今回のアプローチはうまくいかないので注意してください。
コンテナ起動後、localhost:80/events
にアクセスして以下のようなレスポンスが返ってくればOKです。(localhost:3000/events
の結果と同じです。)
Swagger UIをnginxに組み込む
/swagger-ui
にアクセスをしたらSwagger UIの画面が表示されるようにnginxにSwagger UIを組み込んでいきます。
Swagger UIの画面はswagger-ui/distによって構成されています。
dist
配下のファイルをローカルにコピーし、nginxコンテナにバインドマウントすることで、Swagger UIをnginxに組み込みます。
dist
配下のファイルをすべてコピーしてきてもよいのですが、unpkgを利用することでindex.html
のみをコピーするだけでSwagger UIの画面が作成できます。 2
index.html
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
- <link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3/swagger-ui.css" >
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
- <script src="./swagger-ui-bundle.js" charset="UTF-8"> </script>
+ <script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js" charset="UTF-8"> </script>
- <script src="./swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
+ <script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script>
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "https://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
// End Swagger UI call region
window.ui = ui
}
</script>
</body>
</html>
docker-compose.ymlを修正し、作成したindex.html
をnginxのデフォルトの公開ディレクトリである/usr/share/nginx/html
配下に配置します。
docker-compose.yml
services:
api:
(略)
db:
(略)
nginx:
image: nginx:1.19.3
ports:
- '80:80'
command: [nginx-debug, '-g', 'daemon off;']
volumes:
- ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
+ - ./nginx/html/swagger-ui:/usr/share/nginx/html/swagger-ui
depends_on:
- api
volumes:
mysql_data:
/swagger-ui
にアクセスしたらindex.html
が表示されるようnginxの設定を追記します。
server {
listen 80;
server_name localhost;
location / {
proxy_set_header Host localhost;
proxy_pass http://api:3000;
}
# "swagger-ui"にアクセスがあったときの処理
location /swagger-ui {
alias /usr/share/nginx/html/swagger-ui;
}
}
コンテナ起動後、localhost:80/swagger-ui
にアクセスして以下のような画面が表示されればOKです。

ローカルのSwagger SpecがSwagger UI上に反映されるようにする
Swagger UIのurl
を変更することで参照するSwagger Specを変更できます。
ローカルのSwagger Specを参照するように変更します。
index.html
- url: "https://petstore.swagger.io/v2/swagger.json",
+ url: "./api.yml",
上記の変更でローカル環境に配置された./nginx/html/swagger-ui/api.yml
の内容がSwagger UIへ反映されます。
なお、./nginx/html/swagger-ui/
ディレクトリはバインドマウントされているので、ローカルでSwagger Spec編集後、リロードすればコンテナのSwagger UIに変更内容が反映されます。
サンプルとして作成したGET /events
を実行するSwagger Specは以下の通りです。
api.yml
openapi: 3.0.2
info:
title: サンプルAPI
version: 1.0.0
servers:
- url: http://localhost:3000
tags:
- name: イベント
paths:
/events:
get:
tags:
- イベント
description: イベント一覧取得
responses:
200:
description: 成功
content:
application/json:
schema:
type: array
description: イベントの配列
items:
$ref: "#/components/schemas/Event"
components:
schemas:
Event:
type: object
properties:
id:
description: ID
type: integer
format: int64
example: 1
title:
description: タイトル
type: string
example: サンプルイベント
created_at:
description: 作成日
type: string
format: date-time
example: 2020-04-01 10:00
updated_at:
description: 更新日
type: string
format: date-time
example: 2020-04-01 10:00
コンテナ起動後、以下のような画面が表示されればOKです。

CORSの設定をする
Swagger UIはlocalhost:80
、APIはlocalhost:3000
で起動しています。
この状態でSwagger UIからAPIにリクエストを送るとオリジンをまたがっているためAccess to fetch at 'http://localhost:3000/events' from origin 'http://localhost' has been blocked by CORS policy
というエラーが発生します。

クロスオリジン(オリジンをまたぐこと)であることはAPIのエンドポイント(controller
)のrequest.original_url
とrequest.headers[:origin]
でオリジンが異なっていることからもわかります。
app/events_controller.rb
5: def index
=> 6: binding.pry
7: @events = Event.all
8:
9: render json: @events
10: end
> request.original_url
=> "http://localhost:3001/events"
> request.headers[:origin]
=> "http://localhost"
CORSの設定を行い、Swagger UIからAPIリクエストが送れるようにします。
今回はrack-corsを利用してCORSの設定を行ます。
Gemfile
gem 'rack-cors'
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
unless Rails.env.production?
allow do
origins(['localhost', /localhost:\d+\Z/])
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
end
コンテナ起動後、リクエストが正常に返ってくればOKです。

参考: CRUD操作を行うSwagger Spec
- GET /events
- POST /events
- GET /events/{id}
- PATCH /events/{id}
- DELETE /events/{id}
上記のエンドポイントに関するSwagger Specは以下の通りです。
openapi: 3.0.2
info:
title: サンプルAPI
version: 1.0.0
servers:
- url: http://localhost:3000
tags:
- name: イベント
paths:
/events:
get:
tags:
- イベント
description: イベント一覧取得
responses:
200:
description: 成功
content:
application/json:
schema:
type: array
description: イベントの配列
items:
$ref: "#/components/schemas/Event"
post:
tags:
- イベント
description: イベント登録
requestBody:
content:
application/json:
schema:
type: object
properties:
title:
type: string
example: サンプルイベント
responses:
201:
description: 作成
/events/{event_id}:
get:
tags:
- イベント
description: イベント詳細
parameters:
- name: event_id
in: path
description: イベントID
required: true
schema:
type: integer
format: int64
example: 1
responses:
200:
description: 成功
content:
application/json:
schema:
type: object
$ref: "#/components/schemas/Event"
404:
description: event not found
patch:
tags:
- イベント
description: イベント更新
parameters:
- name: event_id
in: path
description: id
required: true
schema:
type: integer
format: int64
example: 1
requestBody:
content:
application/json:
schema:
type: object
properties:
title:
type: string
example: サンプルイベント
responses:
200:
description: 成功
content:
application/json:
schema:
type: object
$ref: "#/components/schemas/Event"
delete:
tags:
- イベント
description: イベント削除
parameters:
- name: event_id
in: path
description: id
required: true
schema:
type: integer
format: int64
example: 1
responses:
204:
description: No Content
components:
schemas:
Event:
type: object
properties:
id:
description: ID
type: integer
format: int64
example: 1
title:
description: タイトル
type: string
example: サンプルイベント
created_at:
description: 作成日
type: string
format: date-time
example: 2020-04-01 10:00
updated_at:
description: 更新日
type: string
format: date-time
example: 2020-04-01 10:00
画面は以下のようになります。
まとめ
以上でAPIとSwagger UIを統合した開発環境の構築手順の紹介を終わります。
- nginxを利用することでSwagger UIとAPIを組み合わせる
- nginxのリバースプロキシ設定は『/etc/nginx/conf.d』配下に作成
- オリジンをまたがるリクエストをする際はCORSの設定が必要になる
- 自作のSwagger SpecをSwagger UIに反映させるにはindex.htmlのurlを変更する
この記事がいいなと思いましたらTwitter(@nishina555)のフォローもよろしくお願いします!