LoginSignup
3
2

More than 3 years have passed since last update.

脱Excel仕様書しつつOpenAPI(Swagger)のYAMLを分割して管理する

Last updated at Posted at 2019-12-13

仕様変更で衰退する人類:hugging:

:blush:スーパーシステムエンジニアのとある1日の流れ:blush:

  1. DB定義が変更になった
  2. DDLなりDB定義書を直す
  3. DBに適用
  4. メンバーにExcelのAPI仕様書変更をしてもらう ←ここで衰退する
  5. 別な変更が足りてなかったらしいので自分で追記 ←ここで衰退する
  6. Excelなのでgitで差分がよくわからず全部見直す ←ここで衰退する
  7. ソースコードを直す ←ここで衰退する
  8. レビューで粗が見つかった場合は4に戻る ←ここで衰退する
  9. 動作確認する ←ここで衰退する
  10. 本番デプロイする ←ここで衰退する
  11. お客様(神様)に一報いれる ←ここで衰退する
  12. お客様(神様)からフィードバックがきて更にDB定義が変わる、1に戻る ←ここで衰退する

OpenAPI (Swagger)便利すぎウケた:hugging:

Excelアレルギーの人のための課題の解決案として、最近Qiitaでも目にするOpenAPI(Swagger)を導入してみる。

・公式
https://swagger.io/resources/open-api/

要するにYAMLファイルを書くだけで

  • いい感じのAPI仕様書(絶妙なデザイン)
  • いい感じのサーバ側テンプレート(いろんな言語で)
  • いい感じのクライアント側テンプレート(いろんな言語で)

を出力できる所謂おもしろツール。

オンライン上でYAMLを書いてその場でレンダリングするSwagger Editorや、サードパーティのコンバーターなどもそこそこある。
V2までSwaggerと呼ばれつつ、V3から公にOpenAPIと呼ぶようになったらしいので、困ったときにググるのがしんどく、それぞれの派閥の抗争が絶えなくて衰退する。

ググり力はさておき、YAMLのメンテだけでAPIの仕様書作成~実装が回るなら衰退しなさそうなので使ってみる。

とりあえず書く:hugging:

こんな感じでYAMLを書く↓

API仕様書.yml

openapi: 3.0.1
info:
  title: API定義書
  description: |
    各APIのインターフェース定義書。  
  version: 1.0.0
paths:
  /login:
    post:
      tags:
        - login
      summary: API001_LOGIN
      description: ユーザID、パスワードを送信し認証処理を行う。
      operationId: API001
      parameters:
        - in: header
          name: Content-Type
          description: リクエストボディの型を指定。application/json を指定する。
          schema:
            type: string
          required: true
        - in: header
          name: Api-Token
          description: 認証用APIトークン。
          schema:
            type: string
          required: true
      requestBody:
        content:
          application/json:
            schema:
              type: object
              title: LoginRequest
              required:
                - user_id
                - password
              properties:
                user_id:
                  description: ID
                  type: string
                  maxLength: 100
                  minLength: 0
                password:
                  description: パスワード
                  type: string
                  maxLength: 25
                  minLength: 25
      responses:
        '200':
          description: 成功時のレスポンス
          content:
            application/json:
              schema:
                type: object
                title: LoginResponse
                properties:
                  result:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      user_token:
                        description: 認証に成功し、発行されたユーザトークン
                        type: string
                        example: thismightbetoken
        '500':
          description: 失敗時のレスポンス
          content:
            application/json:
              schema:
                type: object
                properties:
                  result:
                    type: boolean
                  error:
                    type: object
                    properties:
                      message:
                        description: エラーメッセージ
                        type: string
                      code:
                        description: エラーコード
                        type: string
              example:
                result: false
                error:
                  message: エラーが発生しました。
                  code: ERR01

↑を↓にコピペするといい感じにレンダリングしてくれる。

・Swagger Editor
https://editor.swagger.io/

記法に間違いがあればエラー吐いてくれるし、上部メニューの「Generate Server」「Generate Client」で任意の言語で仕様に沿ったテンプレを吐いてくれる便利っぷり。

長すぎて衰退しそう:hugging:

本題。

ymlファイルがテキスト形式なのでgitで差分も見れるし Excelの5億倍くらいは 開発しやすくなって本当に良かった。
ほんで調子に乗ってpathsの下にどんどんAPIの仕様を追加していくものの、以下で衰退しそうになる。

  • APIが1つ増えるごとに数百行増える
  • 使い回せるRequest/Responseはcomponent化して $ref参照で使い回せる けど、結局それも下の方に書くのでどんどん長くなっていく
  • API毎にいい感じのファイル管理したい

swagger-mergerという概念:hugging:

・swagger-merger
https://www.npmjs.com/package/swagger-merger

ぽい名前のツールがあったのでnpmで突っ込む。

dockerでやりたいならこっち

いい感じにファイルを分割するという概念:hugging:

swagger-merger様がindex.ymlから$refで各ファイルを辿ってマージしてくれるので、API仕様書.ymlを分割して各ディレクトリ、各ファイルに分ける。

・分割したファイルとフォルダ構成

.
│  API仕様書.yml(swagger-mergerで自動生成)
│
└─src
    │  index.yml
    │
    ├─components
    │  │
    │  ├─request
    │  │      LoginRequest.yml
    │  │      LogoutRequest.yml
    │  │
    │  └─response
    │         LoginResponse.yml
    │         CommonSuccessResponse.yml
    │         CommonErrorResponse.yml
    │
    └─paths
         API001_ログイン.yml
         API002_ログアウト.yml

とりあえずこんな感じにしてみる。


src/index.yml
openapi: 3.0.1
info:
  title: API定義書
  description: |
    各APIのインターフェース定義書。  
  version: 1.0.0
paths:
  /login:
    $ref: './paths/API001_ログイン.yml'
  /logout:
    $ref: './paths/API002_ログアウト.yml'
components:
  parameters:
    apiTokenHeader:
      in: header
      name: Api-Token
      description: 認証用APIトークン。
      schema:
        type: string
      required: true
    contentTypeHeader:
      in: header
      name: Content-Type
      description: リクエストボディの型を指定。application/json を指定する。
      schema:
        type: string
      required: true

↑このファイルを根っこに各ファイルを辿ってくれる。


path/API001_ログイン.yml
post:
  tags: 
    - login
  summary: A01_ログイン
  description: ユーザID、パスワードを送信し認証処理を行う。成功時はユーザー固有のトークンを返却する。
  operationId: a01login
  parameters:
    - $ref: '../index.yml#/components/parameters/contentTypeHeader'
    - $ref: '../index.yml#/components/parameters/apiTokenHeader'
  requestBody: 
    content:
      application/json:
        schema: 
          $ref: '../components/request/LoginRequest.yml'
  responses:
    200:
      description: 成功時のレスポンス
      content:
        application/json:
          schema: 
            $ref: '../components/response/LoginResponse.yml'
    500:
      $ref: '../components/response/CommonErrorResponse.yml'

↑path配下はAPI毎の機能概要とか書く。
実際のリクエスト/レスポンスのパラメータはそれぞれ別ファイル管理にした。
ちなみにヘッダーはindex.yml下部のcomponentで共通化してしまっている。


components/request/LoginRequest.yml
type: object
title: LoginRequest
required: # 必須フィールド
  - user_id
  - password
properties:
  user_id: 
    description: ID
    type: string
    maxLength: 100
    minLength: 0
  password: 
    description: パスワード
    type: string
    maxLength: 25
    minLength: 25

↑components/request配下は各APIのリクエスト仕様を記載したymlを配置する。


components/response/LoginResponse.yml
type: object
title: LoginResponse
properties:
  result:
    type: boolean
    example: true
  data:
    type: object
    properties:
      user_token:
        description: 認証に成功し、発行されたユーザトークン
        type: string
        example: thismightbetoken

↑同様にレスポンス。登録系でtrue/falseしか返さないレスポンスや、共通のエラーレスポンスなんかは1ファイル作って使い回す。


満を持してマージ:hugging:

(npm版)

swagger-merger -i ./src/index.yml -o ./API仕様書.yml

「src/index.yml」を基にして各ファイルを辿って「API仕様書.yml」を作成してくれる:hugging:
あとは出力したAPI仕様書.ymlからOpenAPI必殺のジェネレート機能を使って自動生成祭りにする。

前述したSwagger Editorにコピペして「Generate Server」なり「Generate Client」なりして遊んでれば衰退しなさそう。

この辺をコマンドでやりたい場合はopenapi-codegenだのswagger-codegenだの公式で色々あって、だいたい同じようなことがローカルのコマンドでできる。
https://openapi-generator.tech/docs/installation

今はdockerで↓みたいなことをして、ymlのマージ直後にその生成物からHTMLの仕様書を出力してる。

docker run --rm -v %cd%:/local swaggerapi/swagger-codegen-cli-v3:3.0.9 generate -i /local/API仕様書.yml -l html2 -o /local/html

あと見直してて思ったけどindex.ymlの下にあるcomponents.parameters以下でファイル管理すると衰退しにくいかも。

~fin~:hugging:

このあと、共通で使い回せそうだけど微妙に仕様が異なるcomponentとかリクエスト/レスポンスがたくさん出てきてしまい、膨大なファイル管理に追われて人類は衰退する。


:thinking:書いた人のTwitter:thinking:

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2