OpenAPI 3.0以上の仕様について
# 必須
# OpenAPIのどのバージョンを利用して記述しているかを定義する。
# バージョンはセマンティックバージョニングで定義されており、記述もセマンティックバージョニングに従って記述する。
openapi: "3.0.0"
# 必須
# APIのメタデータを定義する。
info:
...
# APIを提供するサーバーを配列で記述する。
servers:
...
# APIを整理するために利用するタグを定義する。
tags:
...
# 必須
# APIとして利用可能なパスおよび操作を定義する
paths:
...
# いろいろなオブジェクトをコンポーネントとして再利用可能な形で定義する。
components:
...
info
info自体は必須項目となる。
- titleは必須であり、文字列型を定義。APIのタイトルを入れる。
- descriptionは、文字列型を定義。APIに関する説明を定義する。マークダウンを使ってリッチテキスト表示もできる。
- versionは必須であり、文字列型を定義。記述している
API設計書(openapi.yaml自身)のバージョン
。
info:
title: "Sample API"
description: "Sample API provides sample function."
version: "1.0.0"
servers
tags
- 必須
- 1URIで1つのタグのみ定義する
- リソース名を単数形で記載する
- キャメルケース
良い例
postid: ""
tag:
- product # GoアプリケーションのhandlerやTypeScriptのclassの単位となる
悪い例
postid: ""
tag:
- products
postid: ""
tag:
- user
- product
operationId
- 必須
-
{HTTPメソッド}{機能物理名}
を記載する - キャメルケース
# GET /products
operationId: getProducts
# GET /products/:product_id
operationId: getProductByProductId
# POST /products
operationId: postProducts
# PUT /products/:product_id
operationId: putProduct
# DELETE /product/:product_id
operationId: deleteProduct
description
- 必須
- APIの機能概要を記載する。
description: IDを指定して商品情報を取得する。
summary
- 必須
-
{機能ID} {機能論理名}
で定義する
summary: XXX-0001 商品参照
security
- 必須
- 認証の要否で以下のように定義する
認証なし
security: []
認証あり
security:
- isAuthorized: []
OAuth認証
securityDefinitions:
isAuthorized:
type: oauth2
flow: accessCode
authorizationUrl: 'https://example.com/authorize'
tokenUrl: 'https://example.com/.well-known/jwks.json'
parameters
parameters
の基本的な種類
- query
- path
- header
- cookie
GET/DELETE
API の場合
- in: PATHはパラメータ
- in: queryはクエリパラメータ
- description: 必須
- name: 物理名を定義する
- 命名規約
- スネークケース
- 原則略語は禁止
- type: arrayの場合、xxx_listやxxx_arrayはNGとする
良い例
- in: path
name: product_id
type: string
description: プロダクトID
required: true
- in: query
name: product_types
type: array
description: プロダクト種別
- in: query
name: is_defective
type: boolean
description: 不良品フラグ
悪い例
- in: path
name: productId # キャメルケースはNG
type: string
description: プロダクトID
required: false # 不要
- in: query
name: product_type_list # xxx_listはNG
type: array
description: プロダクト種別
- in: query
name: defective_flag # trueとfalseがどちらの状態を示すのか不明瞭であるため非推奨
type: boolean
description: 不良品フラグ
POST/PUT API
の場合
- in: リクエストボディ
- in: bodyのみ利用可能
- name: 全てname: bodyとする
- required: リクエストボディが必須でない場合を除いてrequired: trueを定義する
- schema: リクエストモデルをtype: objectで定義する
※ OAS3.0
からリクエストボディはRequest Body Object
としてParameterとは別の概念になったため、in:body
と言う指定方法は廃止される。以下にリクエストボディの方法を示している。
良い例
parameters:
- in: body
name: body
required: true
schema:
$ref: '#/definitions/postProductsRequest'
悪い例
parameters:
- in: body
name: postProductsBody
required: false # 不要
schema:
type: object # TypeScriptのInterfaceの自動生成時に型が適切に定義されない
properties:
product_name:
type: string
バリデーション
必須
- required: 必須パラメータのみrequired: trueを定義する
- default: 必須でないパラメータでもデフォルト値がある場合は定義する
型
- type: 必須
- 文字列:string
- 数値:number
- 整数値:integer
- ブール値:boolean
- 配列:array
- オブジェクト:object
-
type: array
の場合、配列要素items
のtypeも必須 -
type: null
は原則として利用しない - 複数のタイプを定義しない
桁
- 文字列
- 最大桁数:maxLength
- 最小桁数:minLength
- 数値または整数値
- 最大値(境界値を含む):maximum
- 最小値(境界値を含む):maximum
- 境界値を含まない場合のみexclusiveMinimum: trueまたはexclusiveMaximum: trueを定義
- 配列
- 最大要素数:maxItems
- 最小要素数:minItems
- required: trueの場合は原則としてminItems: 1を定義する
区分値
-
enum
必須 - descriptionに区分値の論理名を記載する
# ex. enum
name: gender
type: string
enum:
- '00'
- '01'
- '02'
description: |
性別
00: 不明
01: 男
02: 女
日付/日時/時刻
日付
- ISO8601拡張形式(YYYY-MM-DD)とする
-
example
: 2020-01-31 -
name
: 接尾辞_date -
type
: string -
format
: date
created_date:
type: string
example: '2020-01-31'
format: date
日時
- タイムゾーン指定子付きISO8601形式とする
- 秒精度(YYYY-MM-DDThh:mm:ss+TZD)の場合
-
example
: 2020-01-31T23:59:59+09:00 -
name
: 接尾辞_date_time -
type
: string -
format
: date-time
created_date_time:
type: string
example: '2020-01-31T23:59:59+09:00'
format: date-time
- ミリ秒精度(YYYY-MM-DDThh:mm:ss.sss+TZD)の場合
- 秒精度(YYYY-MM-DDThh:mm:ss+TZD)の場合
-
example
: 2020-01-31T23:59:59.000+09:00 -
name
: 接尾辞_date_time -
type
: string -
pattern
: 必須
created_date_time:
type: string
example: '2020-01-31T23:59:59.000+09:00'
pattern: '^((?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9][0-9][0-9])[+|-]([0-9][0-9]:[0-9][0-9])$'`
時刻
- ISO8601形式(hh:mm)とする
-
example
: 23:59 -
name
: 接尾辞_time -
type
: string -
pattern
: 必須
created_time:
type: string
example: 23:59
pattern: '^(2[0-3]|[01][0-9]):([0-5][0-9])$'
その他
正規表現で表現できる文字列はpatternを利用して定義すること
responses
GET API
の場合
description
- 必須
- HTTPステータスコードのメッセージを記載すること
schema
- HTTPステータス:200の場合
-
type: object
でレスポンスモデルを定義する -
required: 必須
で返る項目を定義する - 再利用可能なモデルを
definitions
配下に定義する- 複合的なモデルを定義する場合は
allOf
を利用する
- 複合的なモデルを定義する場合は
-
良い例
getProductsResponse:
allOf:
- type: object
properties:
products:
type: array
items:
$ref: "#/definitions/product"
required:
- products
- $ref: "#/definitions/pagination"
悪い例
getProductsResponse:
type: array # TypeScriptのInterfaceが適切に定義されません
items:
product:
type: object
properties:
product_id:
type: string
# required: true を定義しないとundefined許容の変数となり不要なType Guardが必要になる
product_name:
type: string
- HTTPステータスコード:400系または500系の場合
- 共通で定義されたレスポンスモデルを利用すること
examples
- ステータスコード:200の場合のみ
application/json
という命名で必須 - 必須項目は必ず値を記載すること
200:
description: OK
schema:
$ref: '#/definitions/getProductsResponse'
examples:
application/json: # Mockサーバのレスポンスになるためフロントエンド開発者も編集する
products:
- product_name: Example Product
create_date: '2020-01-01'
400:
description: Bad Request
schema:
$ref: '#/definitions/ErrorResponse'
500:
description: Internal Server Error
schema:
$ref: '#/definitions/ErrorResponse'
POST/PUT/DELETE API
の場合
description
- 必須
- HTTPステータスコードのメッセージを記載すること
schema
- 原則不要
- 必要な場合は
type: object
でレスポンスモデルを定義する
examples
- schemaを定義した場合のみ記載する
- ステータスコード:200の場合のみapplication/jsonという命名で必須
- 必須項目は必ず値を記載すること
models
リクエストモデル
- URI単位で1モデルを定義する
- 命名規約
- キャメルケース
-
postXxxxRequest
またはputXxxxRequest
# POST /products
postProductRequest:
type: object
properties:
proeuct_name:
type: string
required:
- product_name
# PUT /products/:product_id
putProductRequest:
type: object
properties:
proeuct_id:
type: string
proeuct_name:
type: string
required:
- product_id
レスポンスモデル
- URI単位で1モデルを定義する
- リソースモデルをそのまま利用できる場合は不要
- 命名規約
- キャメルケース
getXxxxResponse
# GET /products
getProductResponse:
type: object
properties:
proeucts:
type: array
items:
$refs: "#/definitions/product"
# GET /products/:product_id
responses:
200:
description: OK
schema:
$ref: "#/definitions/product" # リソースモデルをそのまま利用する場合は不要
リソースモデル
- リソースや共通で利用するエンティティの単位で単数形で定義する
- 命名規約
- キャメルケース
pagination:
type: object
properties:
total_counts:
type: integer
offset:
type: integer
limit:
type: integer
required:
- total_counts
- offset
- limit
HTTPステータス
- 原則としてRFC 7231で定義されているレスポンスステータスコードを利用します
- 以下、設計者が特に意識すべきものを抜粋して記載します。
共通
- バリデーションエラー:400 Bad Request
- 業務エラー:400 Bad Request
- 認証エラー:401 Unauthorized
- 認可エラー:403 Forbidden
- システムエラー:500 Internal Server Error
GET
- 正常系:
200 OK
- 検索系APIで結果0件:
200 OK
- キー検索系APIで対象リソースが存在しないエラー:
404 Not Found
POST
- 正常系(同期):
201 Created
- 正常系(非同期):
202 Accepted
- 一意制約違反エラー:
409 Conflict
- 親リソースが存在しないエラー:
404 Not Found
PUT
- 正常系(同期):
200 OK
- 正常系(非同期):
202 Accepted
- 対象リソースが存在しないエラー:
404 Not Found
DELETE
- 正常系:
204 No Content
- 対象リソースが存在しないエラー:
404 Not Found
HTTPステータスコードの一覧と詳細は以下に詳しく載っている
リクエストボディ
下記以外の方法でリクエストボディを送ることができる。こちらの方法が一般的になる。
/hoge
というpathに対してエンドポイントを作成する例
multipart/form-data
paths:
/hoge:
# post/put/delete
post:
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
foo:
type: string
bar:
type: integer
baz: # ファイル
type: string
format: binary
JSON リクエスト
paths:
/hoge:
# post/put/delete
post:
requestBody:
content:
application/json:
schema:
type: object
properties:
foo:
type: string
bar:
type: integer
XML リクエスト
ここでは、後述の コンポーネントを利用して書いている。
paths:
/hoge:
post:
requestBody:
content:
application/xml:
schema:
$ref: "#/components/schemas/piyo"
components:
schemas:
piyo:
type: object
properties:
foo:
type: string
bar:
type: integer
ファイルアップロード
jpeg ファイルアップロード
paths:
/hoge:
post:
requestBody:
content:
image/jpeg:
schema:
type: string
format: binary
画像 ファイルアップロード
サブタイプにアスタリスクも指定できる。
paths:
/hoge:
post:
requestBody:
content:
image/*:
schema:
type: string
format: binary
サーバー (servers)
API
が動いているサーバーの情報を記述できるほか、Swagger UI
から API を試してみるときに使われる。
servers:
- url: https://example.com/api/v1
配列なので複数書くことができます。
servers:
- url: http://example.com/api/v1
- url: https://example.com/api/v1
複数プロトコルが存在する場合などに、variables
を使って一つにまとめることができる。
servers:
- url: "{protocol}://test{number}.example.com:{port}/api/v1"
variables:
protocol:
default: https
enum:
- http
- https
number:
default: "1"
port:
default: "443"
enum:
- "443"
- "8080"
コンポーネント (components)
コンポーネントは、OpenAPI
の yaml
内で同じ構造が何度も出てくる場合に参照して使いまわせる仕組み。
複数のBodyの形式をサポート
- JSON
- XML
paths:
/hoge:
post:
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/piyo"
application/xml:
schema:
$ref: "#/components/schemas/piyo"
responses:
"200":
description: ok
components:
schemas:
piyo:
type: object
properties:
foo:
type: object
properties:
bar:
type: string
コンポーネントの実装方法のサンプルは以下が参考になる
components:
schemas:
# 共通モデル
parameters:
# GET などで取得範囲を変更するときなどに利用
requestBodies:
# 共通で使う POST データ
responses:
# 共通のレスポンス定義、 paths の中で利用
securitySchemes - 認証
認証情報は、components
の中の securityScheme
s の中に記述する。
それを、認証が必要な要素の security
キーから参照する。
クッキー
を使った認証も書くことができますが、クッキーとして飛ばすキーと値は固定のものしか記述できず、セッション認証のようなことはできない。
この辺の設定は複雑なので、こちらに詳しく書かれている。
基本認証 (Basic認証)
paths:
/hoge:
get:
security:
- BasicAuth: [] # 配列内は権限のスコープ。OAuth と OpenIDConnect 以外は空配列
responses:
"200":
description: ok
components:
securitySchemes:
BasicAuth: # 任意の識別子
type: http
scheme: basic
OAuth 認証
paths:
/hoge:
get:
security:
- OAuth2:
- read
responses:
"200":
description: ok
post:
security:
- OAuth2:
- write
responses:
"200":
description: ok
components:
securitySchemes:
OAuth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://example.com/oauth/authorize
tokenUrl: https://example.com/oauth/token
scopes:
read: "読み取り権限"
write: "書き込み権限"
create_review: "Post new review."
クッキー (Cookie)
APIのテストをするならサーバ側で、テスト用の固定のセッションIDを用意したりする必要がある。
paths:
/hoge:
get:
security:
- CookieAuth: []
responses:
"200":
description: ok
components:
securitySchemes:
CookieAuth:
type: apiKey
in: cookie
name: SSID
JWT
paths:
/hoge:
get:
security:
- bearer: []
responses:
"200":
description: ok
components:
securitySchemes:
bearer:
type: http
description: JWT Token Authenticatio
scheme: bearer
bearerFormat: JWT
Header - ApiKey
paths:
/hoge:
get:
security:
- apikey: []
responses:
"200":
description: ok
#-------------------------------
# Reusable security
#-------------------------------
securitySchemes:
apikey:
type: apiKey
name: x-api-key
in: header
description: API Key Authentication
OpenIDConnect
paths:
/hoge:
get:
security:
- sample_oidc_auth: []
responses:
"200":
description: ok
components:
securitySchemes:
sample_oidc_auth:
type: openIdConnect
openIdConnectUrl: "https://oidc.sample.com/signin"
データ型
type | format | description |
---|---|---|
integer | int32 | 符号付き32ビット整数 |
int64 | 符号付き64ビット整数。いわゆる long 型。APIのレスポンスではうまく処理できないケースがあるので文字列にすることも考える。 | |
number | float | 浮動小数 |
double | 倍精度浮動小数。 | |
string | - | 文字列 |
byte | Base64エンコードされた文字列 | |
binary | バイナリ文字列 | |
date | RFC3339のfull-date形式文字列(=W3C-DTFのComplete Date)。YYYY-MM-DD(例: 1997-07-16)。 | |
date-time | RFC3339のdate-time形式文字列(=W3C-DTFのComplete date plus hours, minutes and seconds)。YYYY-MM-DDThh:mm:ssTZD(例: 1997-07-16T19:20:30+01:00)。 | |
メールアドレス | ||
password | パスアード | |
uuid | UUID | |
boolean | - | 真偽 |
pathsの詳細
paths:
"/shops/{shopId}/reviews":
post:
summary: "Get specified shop's reviews."
description: "Get specified shop's reviews."
tags:
- shops
deprecated: false
parameters:
...
requestBody:
...
responses:
...
security:
...
pathのmethodの種類
- get
- put
- post
- delete
- options
- head
- patch
- trace
deprecated
廃止になった操作かどうかを定義する。
デフォルト false
allOfの使い方
components:
schemas:
Order:
description: '注文'
type: object
required:
- order_id
- description
properties:
order_id:
title: "注文 ID"
type: string
example: "a1b2c3"
description:
title: "注文に関するあれこれ"
type: string
example: "24 -TWENTY FOUR- 10周年記念コンプリートDVD-BOX"
上記のようなレスポンスがあるします。
新規追加とかでリクエストパラメータとして指定する際に、 必要な項目は以下の二つのどちらかになります。
ユーザーの入力されたものを使う
自動的に決まってくる項目になる
前者はレスポンスパラメータとも共通で貼りますが、 後者の項目(id とか作成日時とか)はまだ存在していません。
ここで例えば、リクエストボディ用のOrder
とレスポンス用のOrder
に分ける、みたいな解決方法を取ってしまうと、 段々と schema がコピペだらけになってきてしまい、行数がかなり増えて管理が大変になってきます。
ここでallOf
を使う。
components:
schemas:
OrderId:
description: '注文 ID'
type: object
required:
- order_id
properties:
order_id:
title: '注文 ID'
type: string
example: 'a1b2c3'
OrderModel:
description: '注文モデル'
type: object
required:
- description
properties:
description:
title: '注文に関するあれこれ'
type: string
example: '24 -TWENTY FOUR- 10周年記念コンプリートDVD-BOX'
OrderReponse:
description: '注文'
allOf:
- $ref: '#/components/schemas/OrderId'
- $ref: '#/components/schemas/OrderModel'
OrderRequest:
description: '注文'
allOf:
- $ref: '#/components/schemas/OrderModel'
共通部分を、OrderModel
としてくくり出してしまい、 レスポンスとして返したいものは OrderReponse
という名前にしておきます。
ここで allOf
を指定して、リスト形式で中で使われるものを全部列挙する。
こうすることで、適切にマージされた schema を利用することができるようになります。
allOf, anyOf, oneOfの違い
- allOf
- anyOf
- oneOf
allOf
array の中身すべての schema に合致する
ことが条件。
anyOf
array のうち、ひとつ以上の schema に合致する
ことが条件。
oneOf
array のうち、必ずひとつの schema に合致する
ことが条件。
anyOf と違い、array 中のふたつ以上の schema に合致してしまうとfail
する。
参考記事
exampleの使い方
paths:
/hello:
get:
tags:
- hellos
responses:
'200':
description: 登録されている支払い方法が返される。
content:
application/json:
schema:
oneOf:
- $ref: "#/components/responses/HelloResponse1"
- $ref: "#/components/responses/HelloResponse2"
examples:
response1:
summary: HelloResponse1の通常レスポンスです
value:
success1:
message_title: Response1
message: ハロー1のレスポンスのexampleです
response2:
summary: HelloResponseの特別なレスポンスです
value:
success1:
message_title: Response2
message: ハロー2のレスポンスのexampleです
components:
responses:
HelloResponse1:
type: object
properties:
success1:
type: object
properties:
msg_title:
type: string
example: HelloResponse1
message:
type: string
example: ハロー1のレスポンスです。
HelloResponse2:
type: object
properties:
success1:
type: object
properties:
msg_title:
type: string
example: HelloResponse2
message:
type: string
example: ハロー2のレスポンスです。
エラーハンドリングのベストプラクティス