OpenAPI・Swaggerでインタラクティブな API 仕様ドキュメントを作成する
初めに
今回はOpenAPIを用いたAPI設計書の書き方とSwagger(UI)の使い方についてまとめていきます。
この記事の目標はタイトルの通り、OpenAPI仕様に則ったAPI定義を作成し、SwaggerからそのAPI仕様を確認できることです。
API設計などについては省いており、あくまでOpenAPI仕様の書き方についての解説記事になります。
OpenAPIとは
RESTful APIのインターフェースを記述するためのフォーマットです。
定められたフォーマットに則ってAPIの構造と動作を明確に定義することで
開発者はAPIのエンドポイント、パラメータ、レスポンスなどを正確に文書化し、共有することができます🚀
Swaggerとは
Swaggerは、OpenAPI仕様を視覚化し、対話式のAPIドキュメントを提供するための一連のツールです。
以下のようなものがあります。
ツール | 概要 |
---|---|
Swagger Editor | オンラインツールでOpenAPI仕様に基づいたAPIの記述と検証を行うために使用されます。 |
Swagger Codegen | OpenAPI仕様からクライアントライブラリ、サーバースタブ、APIドキュメントを自動生成するためのツールです。 |
Swagger UI | OpenAPI仕様に基づいて記述されたAPIを視覚化し、対話的にドキュメントを提供するWebベースのUIツールです。 |
今回は、vscodeの拡張機能Swagger Viewerを用いてブラウザで確認します。
この拡張機能はSwagger UIに相当する画面表示を行なってくれます。
インストール後、拡張機能設定でswaggerを検索し、Preview In Browserを有効にすると
ブラウザで確認できるようになります。
サンプルAPI定義の作成と、表示
OpenAPIの記述方法はJSON
かYAML
の2パターンがあり、一般的にはYAML
形式が多いようなので、本記事でもYAML
形式で記述します。
vscodeでymlファイルを作成し、サンプルとして下記コードを貼り付けます。
GET /message
で実行するとメッセージが返ってくるAPI定義になります。
現時点で、コードの意味がわからなくても後ほど解説するので大丈夫です!
openapi: "3.0.3"
info:
title: "Sample API"
version: "1.0.0"
paths:
"/message":
get:
summary: "メッセージ取得"
description: "メッセージを取得します"
responses:
"200":
description: "Success operation"
content:
application/json:
schema:
type: string
example: "Hello World!!"
「Shift」+「Cmd」+「P」でコマンドパレットを開きPreView Swaggerを入力、選択するとブラウザでViewerが立ち上がり、API仕様が確認できました🎉
以上が、OpenAPIに則ったAPI定義ファイルを作成し、Swaggerで確認する流れです!
OpenAPIの書き方
ここからOpenAPIに則ったYAML
ファイルの書き方について解説します。
ルートオブジェクト
主要なオブジェクトは下記の7種類です。
必須項目はopenapi
,info
,paths
の3種類で、このオブジェクトさえ記述していれば最低限のopenAPI仕様書としては一応機能します。
openapi: "3.0.3"
info:
...
servers:
...
tags:
...
paths:
...
security:
...
components:
...
オブジェクト | 概要 |
---|---|
openapi | 必須項目。 使用しているOpenAPIのバージョンを指定します。 |
info | 必須項目。 APIの基本情報(タイトル、バージョン、説明等)を定義します。 |
servers | APIがホストされているサーバーのURLを指定し、APIの実行可能な環境を示します。 |
tags | APIを分類するタグを定義します。 |
paths | 必須項目。 APIの具体的なエンドポイントと、それらに対する操作(GET, POST等)を定義します。 |
security | API全体に適用されるセキュリティスキーム(APIキー、OAuth2等)を定義します。 |
components | OpenAPIの中で、再利用可能なパラメータ、スキーマなど、様々なオブジェクトをコンポーネントとして定義し、API定義全体で使用できるようにします。 |
infoオブジェクト
info
オブジェクトは、APIの基本情報(タイトル、バージョン、説明等)を定義します。
必須なオブジェクトはtitle
,version
の2つです。
version
は、このドキュメントのバージョン情報であることに注意してください。
OpenAPIのバージョン指定は、先述している通りopenapi
オブジェクトで記述します。
description
ではマークダウン形式の記述も可能です✍🏻
info:
title: "Sample API" # 必須。APIのタイトル
version: "1.0.0" # 必須。APIのドキュメントのバージョン情報
description: "このAPIはサンプルの操作とデータを提供します。" # APIの短い説明
termsOfService: "http://example.com/terms/" # APIの利用規約へのURL
contact: # APIの連絡先情報
name: "APIサポート" # 連絡先の名前
url: "http://www.example.com/support" # 連絡先のURL
email: "support@example.com" # 連絡先のEメールアドレス
license: # APIのライセンス情報
name: "Apache 2.0" # ライセンスの名前
url: "https://www.apache.org/licenses/LICENSE-2.0.html" # ライセンスURL
上記infoオブジェクトを記述し、Swagger Viewerで見てみると下記の通りの表示になります。
serverオブジェクト
server
オブジェクトはAPIがホストされているサーバーのURLを指定し、APIの実行可能な環境を示します。配列で定義します。
servers:
- url: "https://api.example.com/v1" # APIをホストしているサーバーのURL
description: "本番環境サーバー" # このサーバーに関する追加情報
- url: "https://staging-api.example.com" # 別のサーバーのURL、例えばステージング環境
description: "ステージング環境サーバー" # このステージングサーバーに関する追加情報
- url: "http://localhost:5000" # ローカル開発用のサーバーURL
description: "開発環境サーバー" # この開発サーバーに関する追加情報
servers
オブジェクト内では、variables
というオブジェクトを使用することもできます。
このvariables
オブジェクトは、サーバーURL内の一部をパラメーター化し動的に置き換えるために使用されるパラメーターのセットです。
servers:
- url: "https://{environment}.example.com/v1" # 動的な部分({environment})を含むサーバーURL
description: "APIサーバー - 環境によって異なります"
variables: # URLの動的部分を定義するための変数
environment: # 変数の名前。urlの{environment}部分
default: "dev" # 変数のデフォルト値。この例では'default'を'dev'としています
description: "APIをホストする環境。例えば、'dev', 'staging', 'prod'など。" # 変数に関する説明。
enum: ["dev", "staging", "prod"] # 変数に許可される値のリスト。
Servers部分に、現在のURLを表すComputed URLや、指定した変数がオプションにあるセレクタが追加されていることがわかります。
pathsオブジェクト
paths
オブジェクトはAPI定義の中心的な部分であり、APIのエンドポイントとそれらに対する具体的な操作(HTTPメソッド)を定義します。
paths:
/items: # エンドポイント
get: # HTTPメソッド
summary: "アイテム一覧の取得" # 操作の簡単な説明
description: "利用可能なアイテムの一覧を返します。" # 操作の詳細な説明
tags: ["items"] # タグの付与。ルートオブジェクトで定義したタグ、または任意のタグが付与可能
deprecated: false # 廃止されたAPIかどうか。trueにするとグレーアウトされた表示になり廃止済であることを明示できる
parameters: # APIリクエストのパラメータを定義するセクション
...
requestBody: # リクエストボディを定義
...
responses: # レスポンスを定義
...
patshオブジェクトの中でも、parameters
、requestBody
、responses
にはさらに細かく
オブジェクトが用意されていますので、詳しくみていきます。
parametersオブジェクト
リクエストにパラメーターが必要な場合に記述します。
/items/{itemId}:
get:
summary: "特定のアイテムの詳細情報を取得"
description: "指定されたIDを持つアイテムの詳細情報を返します。"
tags: ["items"]
parameters: # APIリクエストのパラメータを定義するセクション
- name: itemId # パラメータのプロパティ名
in: path # パラメータの位置: パスパラメータ
required: true # 必須パラメータかどうか
description: "取得するアイテムのID。" # パラメータの説明
schema: { type: string, example: "1021" } # パラメータの型と例。このようにインライン記法もできる
requestBody:
...
responses:
...
Parametersというセクションで、定義したパラメーターの内容が表示されます。
requestBodyオブジェクト
APIにデータを送信する際に使用されるリクエストボディの内容を定義するために使われます。
これは主にPOST、PUTなどのメソッドで利用されます。
下記では、POSTメソッドの定義を追加しました。
paths:
/items:
get:
...
post:
summary: "新しいアイテムを作成"
description: "新しいアイテムを追加します。"
tags: ["items"]
requestBody: # リクエストボディを定義
required: true # このリクエストでボディが必須
content:
application/json: # リクエストボディのメディアタイプ
schema: # リクエストボディの型を定義
type: object # リクエストボディの型: オブジェクト
properties: # オブジェクトのプロパティを定義
name: # プロパティ名を記述
type: string # アイテム名の型: 文字列
description: "アイテムの名前。" # プロパティの説明
description: # プロパティ名を記述
type: string # アイテム説明の型: 文字列
description: "アイテムの詳細説明。" # プロパティの説明
example: # リクエストボディの例
name: "アイテム1"
description: "アイテム1の説明が入ります"
responses:
...
/items/{itemId}:
get:
...
上記のように記述すると下記のようなRequest bodyが表示されます。
responsesオブジェクト
レスポンスを定義していくオブジェクトです。
ステータスコードごとにオブジェクトを用意します。
例
...
responses:
"201":
description: "新しいアイテムを作成しました。" # レスポンスの説明
headers: # レスポンスヘッダー(レスポンスに特殊なヘッダーがあれば記載)
...
content: # レスポンスボディ
...
"400":
description: "不正なリクエスト。入力が無効です。"
...
中でもcontentオブジェクトはレスポンスの中身を定義していくオブジェクトで、
一通り記述した例は下記の通りです。
paths:
/items:
get:
summary: "アイテム一覧の取得"
description: "利用可能なアイテムの一覧を返します。"
tags: ["items"]
deprecated: false
responses: # レスポンスを定義
"200": # HTTPステータスコード 200の場合の処理をこのセクションで定義
description: "成功。アイテム一覧を返します。" # この応答の説明
content: # レスポンスデータを定義
application/json: # レスポンスのコンテントタイプ JSON形式
schema: # レスポンスのスキーマを定義するセクション
type: array # レスポンスの型: 配列
example: # スキーマの例を定義できるセクション
- id: "1"
name: "アイテム1"
- id: "2"
name: "アイテム2"
items: # 配列内のアイテムを定義するセクション
type: object # アイテムの型: オブジェクト
properties: # オブジェクトのプロパティを定義するセクション
id: { type: string, description: "アイテムのID。" } # 型と説明
name: { type: string, description: "アイテムの名前。" } # 型と説明
"400": # HTTPステータスコード 400の場合の処理をこのセクションで定義
description: "不正なリクエスト。パラメータが無効です。" # この応答の説明
tagsオブジェクト
API定義を分類し、整理するために使用されます。
各操作を論理的なグループに分類して、ドキュメント内でのナビゲーションを容易にします。
前章のpathsオブジェクト内にもすでに登場しているtagsオブジェクトですが、ルートオブジェクトでも定義できます。
例) ルートオブジェクトで定義
openapi: "3.0.3"
info:
...
servers:
...
tags:
- name: "items"
description: "アイテムに関連する操作。アイテム取得、作成、更新、削除などでが含まれます。"
- name: "users"
description: "ユーザー管理に関連する操作。ユーザーの作成、プロファイルの取得、更新などが含まれます。"
paths:
...
security:
...
components:
...
tagsを未使用の状態だと全てもAPIがdefaultというタイトルの折りたたみメニューに格納されていますが、上記のように定義することで、折りたたみメニューが追加されます📝
そして下記のようにpatshオブジェクト内で、そのAPI操作に適切なtagsを指定することで、
ルートで定義していた折りたたみメニュー内に集約される形です。
paths:
/items:
get:
tags: ["items"]
...
post:
tags: ["items"]
...
componentsオブジェクト
API定義内のさまざまなスキーマ、レスポンス、パラメータ、セキュリティスキームなどの再利用可能なコンポーネントを格納するために使用されます。
これにより、重複する記述を減らし一貫性を高め、API定義のメンテナンスを容易にします。
主に使用される5つの要素は下記の通りです。
openapi: "3.0.3"
info:
...
servers:
...
tags:
...
paths:
...
security:
...
components:
schemas: # スキーマ定義: リクエストとレスポンスで使用される再利用可能な型を定義します
...
responses: # レスポンス定義: 再利用可能なレスポンスオブジェクトを定義します
...
parameters: # パラメータ定義: 再利用可能なパラメータオブジェクトを定義します
...
requestBodies: # リクエストボディ定義: 再利用可能なリクエストボディオブジェクトを定義します
...
SecuritySchemas: # セキュリティスキーム定義: APIで使用されるセキュリティスキームの詳細を定義します
...
下記のAPI定義では、componentsオブジェクトを使用した例で作成しています。
具体的には、schemas、responsesオブジェクトを定義し、各API操作で、コンポーネントを参照し再利用しています。
コンポーネントを利用するときは、$ref: "#/components/schemas/Error"
のような記述で利用したいコンポーネントを指定し、参照します。
openapi: "3.0.3"
info:
title: "サンプルAPI定義"
version: "1.0.0"
description: "Componentsオブジェクトの使用例を確認するための簡易的API定義"
components: # コンポーネントを定義
schemas: # 再利用したいスキーマを定義
Item: # 具体的なスキーマ名とその内容
type: object
properties:
id:
type: string
description: "アイテムの一意識別子"
name:
type: string
description: "アイテム名"
Error: # 具体的なスキーマ名とその内容
type: object
properties:
code:
type: integer
format: int32
description: "エラーの種類を表すエラーコード"
message:
type: string
description: "エラー詳細メッセージ"
responses: # 再利用したいレスポンスを定義
ItemNotFound:
description: "Item not found."
content:
application/json:
schema:
$ref: "#/components/schemas/Error" # componentsオブジェクト内で定義したErrorスキーマを参照先に指定
paths:
/items:
get:
summary: "アイテム一覧を取得する"
responses:
"200":
description: "アイテム一覧"
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Item" # componentsオブジェクトの中で定義したItemスキーマを参照先に指定
post:
summary: "アイテムを追加する"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Item"
responses:
"201":
description: "アイテム追加"
/items/{itemId}:
get:
summary: "特定のアイテムを取得"
parameters:
- name: itemId
in: path
required: true
description: "アイテムの一意識別子"
schema:
type: string
responses:
"200":
description: "アイテム詳細情報"
content:
application/json:
schema:
$ref: "#/components/schemas/Item" # componentsオブジェクトの中で定義したItemスキーマを参照先に指定
"404":
$ref: "#/components/responses/ItemNotFound" # componentsオブジェクトの中で定義したItemNotFoundレスポンスを参照先に指定
下記の通り、レスポンスのSchemaや404のレスポンスなど、参照先に指定したコンポーネントの内容が表示されており、重複した記述をせず再利用可能であることがわかります🙌
securityオブジェクト
APIへのアクセスを制御するためのセキュリティスキームを定義します。
これにより、APIがどのようにユーザー認証を要求するか、どのような認証方法を使用するかを文書化できます。
components
オブジェクトにsecuritySchemes
というオブジェクトが登場しましたが、
OpenAPI仕様において、ルートレベルのsecurity
オブジェクトは通常、components
オブジェクト内のsecuritySchemes
セクションで定義されたセキュリティスキームを参照するために使用されます。
前章で作成したサンプルコードにルートオブジェクトのsecurity
と、components
オブジェクト内の、securitySchemes
を追記します。
ApiKeyAuth
という名前のAPIキーセキュリティスキームを定義し、API全体に適用しています。
...
security: # グローバルセキュリティ要件: このAPIのすべての操作に適用されます
- ApiKeyAuth: []
components:
...
securitySchemes:
ApiKeyAuth: # セキュリティスキームの名前
type: apiKey
in: header # APIキーがヘッダーに含まれる
name: X-API-KEY # ヘッダーの名前
キーアイコンなどが追加され、クライアントはX-API-KEYヘッダーに適切なAPIキーを提供する必要があることを明示しています。
グローバルセキュリティ要件が不要で、POSTなど特定の操作時に、API key認証を要求する場合は、ルートのsecurity
オブジェクトは削除し、特定操作のAPI定義にsecurity
オブジェクトを追記します。
...
post:
summary: "アイテムを追加する"
requestBody:
...
responses:
...
security:
- ApiKeyAuth: []
まとめ
今回はOpenAPI、SwaggerでのAPI定義の記述法についてまとめましたがいかがだったでしょうか。
OpenAPI、SwaggerでAPI定義を作成することで、ファイルに記述した内容がインタラクティブなUIとして確認できるだけではなく、Gitでのバージョン管理やコンポーネントでの再利用性など、効率よく作成、管理できます。
REST API設計についてもまとめているので、読んでいただけたら嬉しいです。
Discussion