[RUBY] Build an environment of "API development + API verification using Swagger UI" with Docker

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".

About the Docker environment created this time

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.

スクリーンショット 2020-10-25 20.17.16.png

The various versions used this time are as follows.

Create API development environment in Docker

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 the API via nginx

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.

Incorporate Swagger UI into nginx

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.

スクリーンショット 2020-10-25 18.23.07.png

Allow the local Swagger Spec to be reflected on the Swagger UI

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.

スクリーンショット 2020-10-25 18.24.54.png

Set CORS

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.

スクリーンショット 2020-10-25 18.37.42.png

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.

スクリーンショット_2020-10-25_18_39_30.png

Reference: Swagger Spec for CRUD operations

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:

スクリーンショット 2020-10-25 18.44.00.png

Summary

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

Build an environment of "API development + API verification using Swagger UI" with Docker
[First team development ②] Build an environment with Docker
Build an environment of Ruby2.7.x + Rails6.0.x + MySQL8.0.x with Docker
Build a PureScript development environment with Docker
Build a Wordpress development environment with Docker
Build an environment with Docker on AWS
Build an Ultra96v2 development environment on Docker 1
[App development 0.5] [Node.js express Docker] Build an environment for Node.js Express MongoDB using Docker
Build a WordPress development environment quickly with Docker
Build a hot reload development environment with Docker-compose using Realize of Go
[Rails] How to build an environment with Docker
[App development 1] [Node.js express Docker] Build an environment for Node.js Express MongoDB (mongoose) using Docker [December 2020]
How to build an environment of [TypeScript + Vue + Express + MySQL] with Docker ~ Vue edition ~
Build Java development environment with WSL2 Docker VS Code
Build Rails (API) x MySQL x Nuxt.js environment with Docker
Try to build a Java development environment using Docker
I tried to build an environment using Docker (beginner)
Build docker environment with WSL
Build a local development environment for Open Distro for Elasticsearch with multiple nodes using Docker
How to build Rails, Postgres, ElasticSearch development environment with Docker
Display API definition in Swagger UI using Docker + Rails6 + apipie
Build Couchbase local environment with Docker
Build a Node.js environment with Docker
Build PlantUML environment with VSCode + Docker
Development of Flink using DataStream API
Build environment with vue.js + rails + docker
Build Rails environment with Docker Compose
Create SolrCloud verification environment with Docker
Build jooby development environment with Eclipse
Build Unity development environment on docker
Build docker + laravel environment with laradock
Build Go development environment with WSL2 + Docker Desktop + VSCode (Remote --Containers)
I tried to build the environment of PlantUML Server with Docker
I tried to build an http2 development environment with Eclipse + Tomcat
Build an ASP.net Core Web API environment on Docker (VSCode) Part 1
Build a browser test environment using Capybara in the Docker development environment
Build an ASP.NET Core Web API environment on Docker (VSCode) Part 2
Build a development environment for Django + MySQL + nginx with Docker Compose
One file of Docker x Laravel threat! Build a local development environment with the minimum configuration
Laravel development environment construction with Docker (Mac)
Create Spring Boot-gradle-mysql development environment with Docker
Build an authentication proxy server using Docker
[Docker] Build Jupyter Lab execution environment with Docker
Lightweight PHP 7.4 development environment created with Docker
Build TensorFlow operation check environment with Docker
How to build Rails 6 environment with Docker
Build a simple Docker + Django development environment
How to execute with commands of normal development language in Docker development environment
I tried to build a Firebase application development environment with Docker in 2020
[Copy and paste] Build a Laravel development environment with Docker Compose Part 2
How to build an environment for any version of Ruby using rbenv
How to build a Ruby on Rails development environment with Docker (Rails 6.x)
Build a local development environment for Rails tutorials with Docker (Rails 6 + PostgreSQL + Webpack)
[Copy and paste] Build a Laravel development environment with Docker Compose Participation
How to build a Ruby on Rails development environment with Docker (Rails 5.x)
Template: Build a Ruby / Rails development environment with a Docker container (Ubuntu version)
Template: Build a Ruby / Rails development environment with a Docker container (Mac version)
When I tried to build an environment of PHP7.4 + Apache + MySQL with Docker, I got stuck [Windows & Mac]
Self-hosting with Docker of AuteMuteUs in Windows environment
Build a Laravel / Docker environment with VSCode devcontainer
Rails6 [API mode] + MySQL5.7 environment construction with Docker