📝

Dev ContainersでOpenAPIのAPIドキュメントを作る

に公開

本記事では、OpenAPI を利用したAPIドキュメントの管理と、Redocly を活用した視覚的なドキュメント生成について解説します。

RedoclyとOpenAPIについて

OpenAPIとは

OpenAPI はREST APIの仕様を記述するための標準的なフォーマットであり、APIのエンドポイント、リクエスト/レスポンスのデータ構造、認証情報などをYAMLまたはJSONで記述できます。
OpenAPI を活用することで、API のドキュメント生成、コード生成、テスト自動化が容易になります。

Redoclyとは

RedoclyはOpenAPIのドキュメントを視覚的にわかりやすく表示するためのツールです。
静的な API ドキュメントの生成や、ローカルでのプレビューが可能になります。また、VS Code の redocly.openapi-vs-code 拡張機能を使えば、エディタ内で OpenAPI のスキーマ編集を効率的に行えます。


Dev Containers の環境構築

OpenAPIを作るための環境づくりには幾つかの方法がありますが、ローカル環境を汚したくないのでDev Containersを利用します。
OpenAPIドキュメントを開発できるセットアップを行います。
以下の 3 つのファイルについて解説します。

docker-compose.yml

docker-compose.yml
version: '3.8'
services:
  node22-openapi:
    build: ./web
    stdin_open: true
    working_dir: '/app'
    volumes:
      - ../server:/app
    tty: true
    privileged: true

このファイルでは、Node.js 22 の環境を構築するためのコンテナ node22-openapi を定義しています。

  • build: ./web./web ディレクトリから Docker イメージをビルドします。
  • stdin_open: true → コンテナの標準入力を開いたままにする設定。
  • working_dir: '/app' → 作業ディレクトリを /app に設定。
  • volumes: - ../server:/app → ホストの ../server ディレクトリをコンテナの /app にマウント。
  • tty: true → ターミナルを有効化。
  • privileged: true → 特権モードで実行(Docker 内で Docker を実行するため)。

devcontainer.json

devcontainer.json
{
  "name": "node22-openapi",
  "dockerComposeFile": "docker-compose.yml",
  "service": "node22-openapi",
  "workspaceFolder": "/app",
  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },
  "customizations": {
    "vscode": {
      "extensions": [
        "redocly.openapi-vs-code",
        "github.copilot",
        "github.copilot-labs",
        "oderwat.indent-rainbow",
        "mhutchie.git-graph",
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode",
        "vscode-icons-team.vscode-icons",
        "stylelint.vscode-stylelint"
      ],
      "settings": {
        "eslint.options": {
          "extensions": [".js", ".ts"]
        },
        "eslint.validate": ["typescript", "javascript"],
        "editor.codeActionsOnSave": {
          "source.organizeImports": "never",
          "source.fixAll.eslint": "explicit",
          "source.fixAll.stylelint": "explicit"
        },
        "typescript.preferences.importModuleSpecifierEnding": "minimal",
        "eslint.format.enable": true,
        "editor.defaultFormatter": "esbenp.prettier-vscode",
        "prettier.trailingComma": "all",
        "prettier.useEditorConfig": false,
        "prettier.jsxSingleQuote": true,
        "prettier.printWidth": 100,
        "prettier.semi": true,
        "prettier.singleQuote": true,
        "prettier.tabWidth": 2
      }
    }
  }
}

このファイルは Dev Container の設定を定義します。

  • name: Dev Container の名前。
  • dockerComposeFile: 使用する docker-compose.yml のファイル指定。
  • service: 開発コンテナとして使用する node22-openapi を指定。
  • workspaceFolder: コンテナ内で作業するディレクトリを /app に設定。
  • features: docker-in-docker を有効化(コンテナ内で Docker を動かすため)。
  • customizations.vscode.extensions: VS Code にインストールする拡張機能一覧。
  • customizations.vscode.settings: Prettier, ESLint などのフォーマット設定。

Dockerfile

Dockerfile
FROM node:22.14
# openapi-generator-cliを動かすためにjavaが必要
RUN apt update; apt install -y wget software-properties-common apt-transport-https git openjdk-17-jre
RUN npm install -g yml-sorter redoc redoc-cli @openapitools/openapi-generator-cli version-manager
# redocly-cliをインストール(ほかと分けたほうが良さそう)
RUN npm install -g @redocly/cli
# Firebase に deployするため
RUN npm install -g firebase-tools

この Dockerfile は Node.js 22.14 環境をベースに OpenAPI 仕様を処理するためのツールをインストールします。

  • RUN apt update; apt install -y ... openjdk-17-jreopenapi-generator-cli の実行に必要な Java をインストール。
  • RUN npm install -g yml-sorter redoc redoc-cli ... → OpenAPI のツール群をインストール。
  • RUN npm install -g @redocly/cliredocly-cli を個別にインストール。

sample api

openapi.yaml

リモートコンテナを立ち上げたらサンプルサイトを作ります。
src/openapi.yaml

openapi.yaml
openapi: 3.1.0
info:
  title: Sample API
  description: "Sample API description"
  version: 0.1.10

servers:
  - url: http://api.<your-site>.com/v1
    description: 本番サーバー
  - url: http://staging-api.<your-site>.com
    description: 開発サーバー

paths:
  /users:
    get:
      operationId: get-users
      summary: users
      description: |
        **Markdown記法**が使えます。
      security: 
        -  {}
      responses:
        "200":
          description: A JSON array of user names
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
OpenAPI 3.1.0と3.0.3について

OpenAPI 3.1.0 は 2021年2月15日にリリースされましたが、多くのツールは 3.0.3 までにしか対応していないことが多いです。そのため3.0.3を採用しても良いと思います。

操作系やHTML生成など

API ドキュメントの確認やバリデーションには以下のコマンドを使用します。

  • プレビュー

    redocly preview-docs src/openapi.yaml
    

    このコマンドを実行すると、ローカルサーバーで API ドキュメントをプレビューできます。

  • バリデーション

    openapi-generator-cli validate -i ./src/openapi.yaml
    

    これにより、YAMLのOpenAPI 仕様に準拠しているかチェックできます。

  • HTML生成
    HTML生成と更新後の差分比較のため、複数のコマンドをまとめたシェルスクリプトを準備します。

    build
    #!/bin/sh
    
    OUT=./tmp/openapi/openapi.yaml
    # YAMLファイルのリンター
    redocly lint ./src/openapi.yaml
    # 一つのYAMLファイルにまとめる
    openapi-generator-cli generate -g openapi-yaml -i ./src/openapi.yaml -o tmp
    # ソートする
    yml-sorter -i $OUT -o $OUT
      
    npx @redocly/cli build-docs $OUT -o ./dest/index.html
    
    bin/build
    

ビルドするとindex.htmlが作成され、これを公開するとドキュメントを共有できます。
index.htmlをブラウザなどで確認してください。

差分比較や分割など

POSTメソッドを加える

src/openapi.yaml

openapi.yaml
paths:
  /users:
    get:
      operationId: get-users
      summary: users
      description: |
        **Markdown記法**が使えます。
      security: 
        -  {}
      responses:
        "200":
          description: A JSON array of user names
          content:
            application/json:
              schema:
                type: array
                items:
                  type: string
+    post:
+      operationId: post-users
+      summary: users
+      description: |
+        **Markdown記法**が使えます。
+      security: 
+        -  {}
+      requestBody: 
+        content: 
+          propertyName: 
+            schema: 
+              type: object
+              properties: 
+                name: 
+                  type: string
+                  example: "John Doe"
+      responses:
+        "200":
+          description: idを返す
+          content:
+            application/json:
+              schema:
+                type: object
+                properties:
+                  id:
+                    type: integer
+                    example: 1

先ほど作成したbuildスクリプトを変更して変更比較ができるようにします。

新build

#!/bin/sh
OUT=./tmp/openapi/openapi.yaml
BACKUP=./tmp/openapi/openapi.yaml.bak

# オプションを解析する
while getopts "s" opt; do
  case $opt in
    s)
      skip_build=true
      ;;
    *)
      echo "Usage: $0 [-s] (skip build)"
      exit 1
      ;;
  esac
done

# YAMLファイルのリンター
redocly lint ./src/openapi.yaml
# 一つのYAMLファイルにまとめる
openapi-generator-cli generate -g openapi-yaml -i ./src/openapi.yaml -o tmp
# ソートする
yml-sorter -i $OUT -o $OUT

# ビルド処理をスキップする場合、早期終了
if [ "$skip_build" = true ]; then
  exit 0  # 正常終了でスクリプトを終了
fi

# ドキュメントをビルドする
npx @redocly/cli build-docs $OUT -o ./dest/index.html

# 比較用ファイルを作成する
cp $OUT $BACKUP

変更点の説明

変更点 説明
-s オプションの追加 スクリプト実行時に -s を指定すると、ドキュメントのビルドをスキップできるようになった
バックアップファイルの作成 openapi.yaml のコピーを openapi.yaml.bak として保存

OpenAPI ドキュメントの差分比較スクリプト

新しく bin/diff スクリプトを追加し、変更前後の OpenAPI ドキュメントの差分を取得できるようにしました。

#!/bin/sh

OUT=/tmp/openapi/openapi.yaml
OLD=/tmp/openapi/openapi.yaml.bak
DIFF_MOUNTED=/tmp/openapi/diff.md
DIFF=".$DIFF_MOUNTED"

# 最新の OpenAPI YAML を生成(ビルドはスキップ)
./bin/build -s

pwd
docker run --rm -t \
    -v $(pwd)/tmp:/tmp:rw \
    openapitools/openapi-diff:latest $OLD $OUT --markdown $DIFF_MOUNTED

cat $DIFF

差分比較してみます。(初回のみDockerの初期化のため時間がかかります。

bin/diff

以下のような出力結果が得られます。
今回は少量なので比較にも困りませんが、大量にある場合はとても役立ちます。

==========================================================================
==                            API CHANGE LOG                            ==
==========================================================================
                                Sample API                                
--------------------------------------------------------------------------
--                              What's New                              --
--------------------------------------------------------------------------
- POST   /users

--------------------------------------------------------------------------
--                                Result                                --
--------------------------------------------------------------------------
                   API changes are backward compatible                    
--------------------------------------------------------------------------

#### What's New
---

##### `POST` /users

> users

分割

YAMLの課題として、インデントが深くなると記述しづらくなる点があります。。
OpenAPIは分割することが可能です。

openapi.yaml
paths:
  /users:
    $ref: paths/users.yaml

paths/users.yaml

users.yaml
get:
  operationId: get-users
  summary: users
  description: |
    **Markdown記法**が使えます。
  security: 
    -  {}
  responses:
    "200":
      description: A JSON array of user names
      content:
        application/json:
          schema:
            type: array
            items:
              type: string
post:
  operationId: post-users
  summary: users
  description: |
    **Markdown記法**が使えます。
  security: 
    -  {}
  requestBody: 
    content: 
      propertyName: 
        schema: 
          type: object
          properties: 
            name: 
              type: string
              example: "John Doe"
  responses:
    "200":
      description: idを返す
      content:
        application/json:
          schema:
            type: object
            properties:
              id:
                type: integer
                example: 1

更にスキーマを分割し整理するとよりAPI開発が進みます。(次回以降説明予定)

まとめ

この環境を使うことで、Dev Containers内でOpenAPIのAPIドキュメントを作成し、redocredocly-cliを活用して視覚的に確認することができます。
また、openapi-generator-cliを用いたコード生成も可能になります。
Dev Containers を利用することで、ローカル環境を汚さずに効率的なAPIドキュメント開発ができます。

template

https://github.com/last-fortune/devcontainer-openapi-template

Discussion