You can use Swagger to document the REST API specifications. In Swagger, the file that documents the REST API specification is called Swagger Spec.
Swagger UI is a tool that generates static pages that reflect Swagger Spec information. The Swagger UI not only visualizes Swagger Spec information, but also provides the ability to execute REST APIs from the screen.
This time, I will introduce the procedure for building a Docker environment that allows you to perform a series of tasks such as "Documenting the API under development with Swagger Spec → Verifying API requests from the Swagger UI screen that reflects Swagger Spec information".
The specifications are as follows.
--Environment can be prepared just by docker-compose up --The Swagger UI screen is displayed with "/ swagger -ui" --Except for "/ swagger-ui", use the endpoint for API --After editing Swagger Spec, reloading will reflect the changes in Swagger UI --API is created in API mode of Ruby on Rails --DB uses MySQL
By using nginx as a reverse proxy, you can combine the API development environment with the Swagger UI. The figure is as follows.
The various versions used this time are as follows.
API development using Rails API mode, referring to Rails 6 x MySQL 8 Docker environment construction procedure created in API mode Create an environment.
The Dockerfile and docker-compose.yml are as follows.
Dockerfile
FROM ruby:2.7.1
#Working directory/rails_api_Designated as swagger
WORKDIR /rails_api_swagger
#Copy local Gemfile to Docker
COPY Gemfile* /rails_api_swagger/
# /rails_api_bundle install on the swagger directory
RUN bundle install
docker-compose.yml
version: '3'
services:
api: #Container launched by Ruby on Rails
build: .
ports:
- '3000:3000' #Make it accessible on localhost port 3000
volumes:
- .:/rails_api_swagger #Application file synchronization
depends_on:
- db
command: ["./wait-for-it.sh", "db:3306", "--", "./start.sh"]
db: #Container that MySQL starts
image: mysql:8.0.21
volumes:
- mysql_data:/var/lib/mysql #Data persistence
- ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
command: --default-authentication-plugin=mysql_native_password #Set the authentication method to 8 series or earlier.
environment:
MYSQL_USER: 'webuser'
MYSQL_PASSWORD: 'webpass'
MYSQL_ROOT_PASSWORD: 'pass'
MYSQL_DATABASE: 'rails_api_swagger_development'
volumes:
mysql_data: #Data volume registration
** Since the function to generate static pages is excluded in Rails API mode, Swagger UI cannot be directly embedded in Rails application **, but if you use this method, you can use Swagger UI even in API mode. I can do it.
Create a sample API.
#Start container in background
$ docker-compose up -d
#Create functions (models, views, controllers) to operate Events at once
$ docker-compose exec api rails g scaffold event title:string
#Create events table
$ docker-compose exec api rails db:migrate
#Create a record of events in rails console
$ docker-compose exec api rails c
> event = Event.new(title: 'Sample event')
> event.save
It is OK if you access localhost: 3000 / events
and get the following response.
Set up a reverse proxy so that you can access your Rails application via nginx.
default.conf
server {
listen 80;
server_name localhost;
# "/"Processing when there is access to
location / {
proxy_set_header Host localhost; #Set the access source host to localhost
proxy_pass http://api:3000; #Send a request to port 3000 of the api container
}
}
The nginx configuration file is /etc/nginx/nginx.conf
.
As you can see from the description ʻinclude /etc/nginx/conf.d/*.conf;in the configuration file, it is called
.conf under
/etc/nginx/conf.d` in the configuration file. The extension settings are also loaded.
In other words, you can use the nginx container as a reverse proxy by placing the configuration file created this time under /etc/nginx/conf.d
.
Add an nginx container to docker-compose.yml.
docker-compose.yml
services:
api:
(Abbreviation)
db:
(Abbreviation)
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;']
is the boot method in debug mode. [^ nginx-debugmode]
Docker image of swagger-ui also uses nginx, but ʻinclude / etc / in nginx.conf
There is no description of nginx / conf.d / *. Conf; `.
So, please note that this approach will not work if you use ** swagger-ui Docker image **.
After starting the container, access localhost: 80 / events
and get the following response.
When you access / swagger-ui
, we will incorporate Swagger UI into nginx so that the Swagger UI screen will be displayed.
The Swagger UI screen is made up of swagger-ui / dist.
Include Swagger UI in nginx by copying the files under dist
locally and binding mounting them in the nginx container.
You can copy all the files under dist
, but by using unpkg, you can copy only ʻindex.html` and the Swagger UI screen. Can be created. [^ swagger-ui-installation]
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>
Modify docker-compose.yml and place the created ʻindex.htmlunder the default public directory of nginx,
/ usr / share / nginx / html`.
docker-compose.yml
services:![Screenshot 2020-10-25 18.23.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140792/22cf7096-e237-7c2e-3aa5-0959d4776657.png)
api:
(Abbreviation)
db:
(Abbreviation)
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:
Add nginx settings so that ʻindex.html is displayed when you access
/ swagger-ui`.
default.conf
server {
listen 80;
server_name localhost;
location / {
proxy_set_header Host localhost;
proxy_pass http://api:3000;
}
# "swagger-ui"Processing when there is access to
location /swagger-ui {
alias /usr/share/nginx/html/swagger-ui;
}
}
After starting the container, access localhost: 80 / swagger-ui
and it is OK if the following screen is displayed.
You can change the referenced Swagger Spec by changing the Swagger UI ʻurl`.
Change it to refer to the local Swagger Spec.
index.html
- url: "https://petstore.swagger.io/v2/swagger.json",
+ url: "./api.yml",
With the above changes, the contents of ./nginx/html/swagger-ui/api.yml
placed in the local environment will be reflected in the Swagger UI.
The ./nginx/html/swagger-ui/
directory is bind-mounted, so if you edit the Swagger Spec locally and reload it, the changes will be reflected in the Swagger UI of the container. ** **
The Swagger Spec that executes GET / events
created as a sample is as follows.
api.yml
openapi: 3.0.2
info:
title:Sample API
version: 1.0.0
servers:
- url: http://localhost:3000
tags:
- name:Event
paths:
/events:
get:
tags:
-Event
description:Get event list
responses:
200:
description:success
content:
application/json:
schema:
type: array
description:Array of events
items:
$ref: "#/components/schemas/Event"
components:
schemas:
Event:
type: object
properties:
id:
description: ID
type: integer
format: int64
example: 1
title:
description:title
type: string
example:Sample event
created_at:
description:Created date
type: string
format: date-time
example: 2020-04-01 10:00
updated_at:
description:Update date
type: string
format: date-time
example: 2020-04-01 10:00
After starting the container, it is OK if the following screen is displayed.
The Swagger UI is running on localhost: 80
and the API is running on localhost: 3000
.
If you send a request from Swagger UI to API in this state, it straddles the origin, so ʻAccess to fetch at'http: // localhost: 3000 / events' from origin'http: // localhost' has been blocked by CORS policy` Error occurs.
Set up CORS so that API requests can be sent from the Swagger UI. This time, use rack-cors to set 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
After starting the container, it is OK if the request is returned normally.
The Swagger Spec for the above endpoints is:
api.yml
openapi: 3.0.2
info:
title:Sample API
version: 1.0.0
servers:
- url: http://localhost:3000
tags:
- name:Event
paths:
/events:
get:
tags:
-Event
description:Get event list
responses:
200:
description:success
content:
application/json:
schema:
type: array
description:Array of events
items:
$ref: "#/components/schemas/Event"
post:
tags:
-Event
description:Event registration
requestBody:
content:
application/json:
schema:
type: object
properties:
title:
type: string
example:Sample event
responses:
201:
description:Create
/events/{event_id}:
get:
tags:
-Event
description:Event details
parameters:
- name: event_id
in: path
description:Event ID
required: true
schema:
type: integer
format: int64
example: 1
responses:
200:
description:success
content:
application/json:
schema:
type: object
$ref: "#/components/schemas/Event"
404:
description: event not found
patch:
tags:
-Event
description:Event update
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:Sample event
responses:
200:
description:success
content:
application/json:
schema:
type: object
properties:
activity:
$ref: "#/components/schemas/Event"
delete:
tags:
-Event
description:Event deletion
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:title
type: string
example:Sample event
created_at:
description:Created date
type: string
format: date-time
example: 2020-04-01 10:00
updated_at:
description:Update date
type: string
format: date-time
example: 2020-04-01 10:00
The screen looks like this:
This concludes the introduction of the procedure for building a development environment that integrates the API and Swagger UI.
--Combine Swagger UI and API by using nginx --Nginx reverse proxy settings are created under "/etc/nginx/conf.d" --CORS settings are required when making cross-origin requests --Change the url of index.html to reflect your own Swagger Spec in Swagger UI
I'm on Twitter (@ nishina555). I hope you will follow me!
Recommended Posts