Edited at

10分くらいでなんとなくわかるSwagger

ある程度の規模を想定した、Swaggerで具体的に内容を書いていけるようになるまでの概要と構成についてのまとめ。

具体的な書き方はあまり難しくないが簡単でもないため、そこそこ学習コストかけて慣れるしかない。公式のドキュメントが割としっかりしていてサンプルも充実しているからそこまで困らないように感じている。

image.png


What's Swagger

若干ややこしいが大きく2つの意味がある。


  1. JSON or YAMLで記載する「RESTful APIドキュメントの標準規格」のバージョン2.xの頃の名称


    • 3.0からはOpenAPI(OpenAPI Specification)という名称になっている

    • 「Open API Initiative」という団体が推進している

    • いくつかある標準規格の中で世界標準になりつつある



  2. Swagger or OpenAPIのフォーマットでRESTful APIドキュメントを作成するためのツール群



    • Swagger UI: 標準フォーマットを良い感じにHTML形式に変換してブラウザ上で表示するためのViewer


      • 画面上からパラメータを指定してAPIを実行することも可能




    • Swagger Editer: 上記のUIとセットになっていてリアルタイムに描画を確認しながらオンライン上で編集できるエディタ


      • 補完やバリデーションチェックもしてくれて優秀

      • 当然だが、別にこれを使わなくても書ける




    • Swagger Codegen: 標準フォーマットの定義に則った動作をするAPIサーバと各種言語用のAPI実行コードを生成してくれるらしい


      • 試していなくて実用的な使い方の情報も見当たらなかったためよくわからない



    • 他にもあるようだが上記の3つが代表的なツール






Sample (YAML)

https://editor.swagger.io

上記のURL(Swagger Editor)に下記のYAMLをコピペするとSwagger UIで見れる。

公式のサンプルからだいぶ削ったシンプルなサンプル。

openapi: 3.0.0

info:
title: Swagger Petstore
version: 1.0.0
servers:
- url: http://192.168.123.45/v2
tags:
- name: user
paths:
/user/{username}:
get:
tags:
- user
summary: Get user by user name
parameters:
- name: username
in: path
description: 'The name that needs to be fetched. Use user1 for testing. '
required: true
schema:
type: string
responses:
200:
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/User'
put:
tags:
- user
summary: Updated user
parameters:
- name: username
in: path
description: name that need to be updated
required: true
schema:
type: string
requestBody:
description: Updated user object
content:
'*/*':
schema:
$ref: '#/components/schemas/User'
required: true
responses:
400:
description: Invalid user supplied
content: {}
components:
schemas:
User:
type: object
properties:
id:
type: integer
format: int64
username:
type: string


Small Practice

実行するための最小構成

serversは指定しないとlocalhostになるため、APIサーバとSwagger UIを動かす場所が同じなら省略可

openapi: 3.0.0  # Swagger or OpenAPIのバージョンの指定(必須)

info:
title: Swagger Petstore # ページタイトル(必須)
version: 1.0.0 # リリースのバージョン(必須)
servers:
- url: http://192.168.123.45/v2 # APIサーバのURL(無くてもエラー出ないがほぼ必須)
paths: # 各種APIの仕様を記載していく最もメインとなる部分
/hoge: # パス
get: # メソッド
responses: # 1つ以上のステータスコードとdescriptionが必須
200:
description: hogehoge

ただし、実際はpathsにすべて記載していくと肥大化して編集や管理が大変になるため、具体的な内容はcomponentsに外出ししてpathsの方では参照させるだけにするのが一般的な様子。

openapi: 3.0.0 

info:
title: Swagger Petstore
version: 1.0.0
servers:
- url: http://192.168.123.45/v2
paths:
/hoge:
get:
responses:
200:
# componentsを参照させる
$ref: '#/components/responses/GenericSuccessResponse'

# 以下を追加(実際はパラメータ定義やリターンの定義なども書いていくので結構長くなる)
components:
responses:
GenericSuccessResponse:
description: hogehoge


Divide Files

※公式にはファイル分割管理のベストプラクティスは特に書かれていません(たぶん)

そのため以下は公式の仕様やネットの情報を見ながら試行錯誤して考えた方法になります。

componentsに外出ししていってもAPIの数だけpathsは増えていくし、使い回せない部分も多いので1つのAPIに対してそれなりの量のcomponentsを書いていくことになり、すぐにファイルが肥大化してしまう問題が発生する。公式のPetstoreのサンプルですら700行を超えている。

そこで、componentsの参照で使用した$refは外部ファイルを参照することも可能であることを利用し、ファイルの分割管理を行う。

ただし、分割してしまうと1ファイルではなくなるためSwagger Editorで編集することができなくなるデメリットがある。

どちらが良いかは規模による。

# ディレクトリ構成

./
├─ index.yaml # Swagger UIで見るファイル
├─ paths/
│ └─ hoge.yaml # /hogeの仕様書の雛形となるファイル
└─ components/
└─ hoge.yaml # /hogeの具体的な仕様を定義したファイル

# ./index.yaml

openapi: 3.0.0
info:
title: Swagger Petstore
version: 1.0.0
servers:
- url: http://192.168.123.45/v2
paths:
/hoge:
$ref: './paths/hoge.yaml'

# ./paths/hoge.yaml

get:
responses:
200:
# 「./paths/hoge.yaml」から見た相対パスになることに注意
$ref: '../components/hoge.yaml#/responses/GenericSuccessResponse'

# ./components/hoge.yaml

responses:
GenericSuccessResponse:
description: hogehoge

しっかり作成するとcomponentsが肥大化してしまうおそれがあるが、その場合は更に細分化することを検討した方がいいかもしれない。

また、この場合は複数のAPIでcomponentsを再利用するとどこで何を使っているのかが複雑になってしまうため、数ヶ所でしか利用しない場合は再利用せずにコピペしたり、多くの場所で再利用するものはcommon.yamltags_XXX.yamlのような抽象的な名前のファイルに定義するなどの設計にするのがよいと思う。

ちなみにinfopathsをまとめて別ファイルにして参照することはできない。

これは明示的に公式にも書かれている仕様。

https://swagger.io/docs/specification/using-ref/

# Incorrect!

info:
$ref: info.yaml
paths:
$ref: paths.yaml


Divide Files -another version-

要は複数のYAMLから1つのYAMLを生成すればよいので、ファイル分割のアイデアはいろいろ考えられる。

ただ、少なくとも現状では上記の方法で実現するのが最も無難だと思う。


  • 分割したSwaggerのファイルを1つのファイルにマージするツールを使う


    • いずれもローカルでマージコマンドを実行する必要あり

    • 編集してから確認までの手間がかかって煩雑になってしまう問題がある



  • システム構成やコード作り込み頑張ってマージ自動化やリアルタイム生成する


    • CI回したり、各種YAMLを読み込んでマージして返すAPIを作ってUIでそのAPIのURLを参照させれば実現は可能

    • 本当に必要なら検討してもよいが、複雑になりがちなのでなるべく避けたい




Link

参考にしたリンク(主にQiita)