From dc748c035eff2199ac471e53488d72d38ede3178 Mon Sep 17 00:00:00 2001 From: Hiroki Takeda Date: Thu, 27 Jun 2024 11:39:52 +0900 Subject: [PATCH] Revised for Public Release (#128) * docs: fix to include a space before and after alphabets by textlint * docs: remove a period mark in list item * docs: fix typographical errors * docs: auto fix by text lint preset-ja-technical-writing * docs: revise the info part * docs: revise the path part * docs: revise the components part * docs: fix typographical errors * docs: revise the design point part * docs: activate link * docs: correct review findings --- .../OpenAPI_Specification_3.0.3.md | 1185 +++++++---------- documents/forOpenAPISpecification/README.md | 8 +- 2 files changed, 518 insertions(+), 675 deletions(-) diff --git a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md index 00645347..72217c3c 100644 --- a/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md +++ b/documents/forOpenAPISpecification/OpenAPI_Specification_3.0.3.md @@ -18,8 +18,8 @@ meta: 本ドキュメントは [OpenAPI Specification 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)に則った API ドキュメントを記述する際のコーディング規約をまとめている。 旧バージョンである[OpenAPI Specification 2.0 の規約](OpenAPI_Specification_2.0.md)も存在するため、v2を使用している場合はそちらを参照されたい。 -本規約は[前提条件](prerequisite.md)に基づいて作成されており、ToC向けの LSUDs(Large Set of Unknown Developers)向けの Web API には適合しない場合もあるのでご留意いただきたい。 -Web APIの設計自体はこの規約の範囲外であるが、[API 設計標準](API_Design.md) にステータスコード等の標準を記載しているため、必要に応じて参考にされたい。 +本規約は[前提条件](prerequisite.md)に基づいて作成されており、ToC 向けの LSUDs(Large Set of Unknown Developers)向けの Web API には適合しない場合もあるのでご留意いただきたい。 +Web API の設計自体はこの規約の範囲外であるが、[API 設計標準](API_Design.md) にステータスコード等の標準を記載しているため、必要に応じて参考にされたい。 ## ファイルフォーマット @@ -79,16 +79,15 @@ openapi: 3.0 ### info > title -WebAPI の総称を記載する。 +Web API の総称を記載する。 +システム名やサービス名 + API のような命名を推奨する。 -- システム名やサービス名 + API のような命名を推奨する。 - - 良い例: +良い例: - ```yaml - info: - title: X System API - ``` +```yaml +info: + title: X System API +``` ### info > description @@ -96,10 +95,11 @@ Web API が提供する機能の概要・想定する利用者やユースケー ### info > version -この API 仕様のドキュメントのバージョンを記載する。アプリケーションのバージョン(git tag やリリースで管理するようなバージョン)とは別である。 +この API 仕様のドキュメントのバージョンを記載する。 +アプリケーションのバージョン(git tag やリリースで管理するようなバージョン)とは別である。 -- `major.minor` 形式を推奨する。 -`0.1 `固定で開発を進め、サービスのリリース時に `1.0` とし、その後の項目やオプション、パスの追加ごとにマイナーバージョンをインクリメントしていく。 +- `major.minor` 形式を推奨する + `0.1 `固定で開発を進め、サービスのリリース時に `1.0` とし、その後の項目やオプション、パスの追加ごとにマイナーバージョンをインクリメントしていく 良い例: @@ -108,7 +108,7 @@ Web API が提供する機能の概要・想定する利用者やユースケー version: 1.0 ``` -- もし他チームへの API ドキュメントの頻繁な共有が必要であれば、`major.minor` の代わりに `YYYY.MM.DD` の日付形式も許容する。 +- 他チームへの API ドキュメントの頻繁な共有が必要な場合は `YYYY.MM.DD` の日付形式も許容する 良い例: @@ -121,9 +121,9 @@ Web API が提供する機能の概要・想定する利用者やユースケー Web API を提供するサーバの情報を記載する。 -- `url`, `description` を必須項目とする。 +- `url`, `description` を必須項目とする - ステージ(local, develop, staging など)が複数ある場合は各ステージ分の情報を記載する。 -- SSKDs 向けの Web API 開発においては本番環境の URL を不用意に公開したくないケースが多く、記載は避けるべきである。 +- SSKDs 向けの Web API 開発においては本番環境の URL を不用意に公開したくないケースが多く、記載は避けるべきである 良い例: @@ -149,9 +149,53 @@ servers: API の利用可能なエンドポイントと操作方法を記載する。 -- API ごとに機能IDを定義している場合、`paths` 配下の各パスは機能 ID の昇順に定義する。 -- URL パスが複数の単語からなる場合、ケバブケースで表現する。 -- HTTP メソッドは `GET`, `POST`, `PUT`, `PATCH`, `DELETE` の順に定義する。 +- API ごとに機能 ID を定義している場合、`paths` 配下の各パスは機能 ID の昇順に定義する + + 良い例: + + ```yaml + paths: + /users: + get: + summary: API-101 ユーザ一覧取得 + /products: + get: + summary: API-201 商品一覧取得 + ``` + + 悪い例: + + ```yaml + paths: + /products: + get: + summary: API-201 商品一覧取得 + /users: + get: + summary: API-101 ユーザ一覧取得 + ``` + +- URL パスが複数の単語からなる場合、ケバブケースで表現する + + 良い例: + + ```yaml + paths: + /product-owners: + get: + ... + ``` + + 悪い例: + + ```yaml + paths: + /productOwners: + get: + ... + ``` + +- HTTP メソッドは `GET`, `POST`, `PUT`, `PATCH`, `DELETE` の順に定義する 良い例: @@ -175,31 +219,34 @@ API の利用可能なエンドポイントと操作方法を記載する。 ... ``` -- HTTPメソッドの配下に定義されるオペレーションオブジェクトは、下記の項目を必須項目とする。 - - | フィールド名 | 必須 | 記載内容 | - | ------------ | :--: | ---------------------------------------- | - | tags | ○ | API の論理的なグループ | - | operationId | ○ | API の利用可能なエンドポイントと操作方法 | - | summary | ○ | API の操作概要 | - | description | | API の振る舞いの詳細や注意点を記載する。 | - | parameters | | API のリクエストパラメータ | - | requestBody | | API のリクエストボディ | - | response | ○ | API のレスポンス | - | security | | API のセキュリティ機構 | - - -### paths > tags +- HTTP メソッドの配下に定義されるオペレーションオブジェクトは、下記の項目を必須項目とする + +| フィールド名 | 必須 | 記載内容 | +|--------------| :--: |------------------------| +| tags | ○ | API の論理的なグループ | +| summary | ○ | API の操作概要 | +| description | ○ | API の振る舞いの詳細や注意点 | +| externalDocs | | API に関する追加の文書 | +| operationId | ○ | API の利用可能なエンドポイントと操作方法 | +| parameters | | API のリクエストパラメータ | +| requestBody | | API のリクエストボディ | +| responses | ○ | API のレスポンス | +| callbacks | | | +| deprecated | | API が非推奨であることの宣言 | +| security | | API のセキュリティ機構 | +| servers | | API に対応する代替サーバ | + +### paths > {path} > {method} > tags API の論理的なグループを指定する。 -- タグオブジェクトとして事前定義したタグの中から選択すること。 +- タグオブジェクトとして事前定義したタグの中から選択する 良い例: ```yaml paths: - /users/me: + /users: get: tags: - users @@ -212,7 +259,7 @@ API の論理的なグループを指定する。 ```yaml paths: - /users/me: + /users: get: tags: # タグオブジェクトとして定義されていないタグが指定されている @@ -221,13 +268,13 @@ API の論理的なグループを指定する。 tags: [] ``` -- 1 API につき 1つのタグを指定すること。 +- 1 API につき 1つのタグを指定する 良い例: ```yaml paths: - /users/me: + /users: get: tags: - users @@ -238,7 +285,7 @@ API の論理的なグループを指定する。 ```yaml paths: - /users/me: + /users: get: # 複数のタグが指定されている tags: @@ -247,194 +294,354 @@ API の論理的なグループを指定する。 ... ``` -### paths > operationId +### paths > {path} > {method} > summary + +API の操作概要を記載する。 + +- API ごとに機能 ID や機能名があるのであれば記載する + + 良い例: + + ```yaml + paths: + /users: + get: + summary: API-001 ユーザ一覧取得 + ``` + +### paths > {path} > {method} > description + +API の振る舞いの詳細や注意点を記載する。 +別途参照させるべき設計書があるのであれば、設計書へのリンクを記載しても良い。 + + 良い例: + + ```yaml + paths: + /users: + get: + description: [API詳細設計書(API-001)](https://example.com/API-001.md) + ``` + +### paths > {path} > {method} > operationId API を識別するための一意な文字列を記載する。 -- HTTP メソッドとURLパスをアッパーキャメルケースで表現する。 - ただしOpenAPI ドキュメントのエディタとして広く使用されるStoplightが提供する[Linter](https://docs.stoplight.io/docs/spectral/674b27b261c3c-overview)の定義としてケバブケースが標準になっているため、Stoplightを使用する場合はケバブケースで表現しても良い。 +- HTTP メソッドと URL パスの組み合わせをアッパーキャメルケースで表現する 良い例: ```yaml paths: - /users/me: + /users: get: - operationId: get-users-me + operationId: GetUsers ... /products/{product_id}: put: - operationId: put-products-product-id + operationId: PutProductsProductId ... ``` + 悪い例: + + ```yaml + paths: + /users: + get: + operationId: get_users + ... + ``` + +- OpenAPI ドキュメントエディタとして広く使用される Stoplight が提供する[Linter](https://docs.stoplight.io/docs/spectral/674b27b261c3c-overview)の定義としては、ケバブケースが標準になっているため、Stoplight を使用する場合はケバブケースで表現しても良い + +### paths > {path} > {method} > parameters + +API のリクエストパラメータを記載する。 + +- クエリパラメータはスネークケースで表現する + 良い例: ```yaml paths: - /users/me: + /users: get: - operationId: get_users_me ... + parameters: + - name: account_type + in: query ``` -### paths > summary + 悪い例: -API の操作概要を記載する。 + ```yaml + paths: + /users: + get: + ... + parameters: + - name: account-type + in: query + ``` -- 機能 ID や機能名があるのであれば記載する。 +- クエリパラメータは HTTP メソッドが `GET`, `DELETE` の場合にのみ指定する 良い例: ```yaml paths: - /users/me: + /users: get: - summary: API-001 ユーザアカウント取得 + ... + parameters: + - name: account_type + in: query ``` -### paths > description + 悪い例: -APIの振る舞いの詳細や注意点を記載する。 -別途参照させるべき設計書があるのであれば、設計書へのリンクを記載しても良い。 + ```yaml + paths: + /users: + post: + ... + parameters: + - name: acaccount_type + in: query + ``` -### paths > parameters +- ヘッダはハイフンを区切り文字とするパスカルケースで表現する -API のリクエストパラメータ(パスパラメータ、クエリパラメータ、ヘッダ)を記載する。 + 良い例: -- HTTP メソッドが `GET`, `DELETE` の場合にのみ指定する。 -- パスパラメータはスネークケースで表現する。 -- クエリパラメータはスネークケースで表現する。 -- ヘッダはハイフンを区切り文字とするパスカルケースで表現する。 + ```yaml + paths: + /users: + post: + ... + parameters: + - name: Content-Type + in: header + ``` -### paths > requestBody + 悪い例: + + ```yaml + paths: + /users: + post: + ... + parameters: + - name: ContentType + in: header + ``` + +### paths > {path} > {method} > requestBody API のリクエストボディを記載する。 -- リクエストボディを記載する。 - 標準仕様の [describing-request-body](https://swagger.io/docs/specification/describing-request-body/) の章にも記載がある通り、リクエストボディは `POST`、`PUT`、`PATCH` で使用され、`GET`、`DELETE`、`HEAD` には使用できない。 -- requestBodyの定義は、`components/requestBodies` で宣言し、`$refs` で参照する。 -- requestBodyの命名は、`Req` というプレフィクスと、`Body` というサフィックスで終える必要がある。 +- 標準仕様の [describing-request-body](https://swagger.io/docs/specification/describing-request-body/) の章にも記載がある通り、リクエストボディは HTTP メソッドが `POST`, `PUT`, `PATCH` の場合のみ指定する -```yaml -paths: - /products: - post: - operation_id: post-products - requestBody: - $ref: '#/components/requestBodies/ReqPostProductsBody' - ... -``` + 良い例: + + ```yaml + paths: + /users: + post: + ... + requestBody: + required: true + content: + application/json: + ... + ``` -### paths > responses + 悪い例: -API のレスポンスを記載する。 + ```yaml + paths: + /users: + get: + ... + # HTTP メソッドが GET の場合にリクエストボディを指定 + requestBody: + ... + ``` -- OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `components` オブジェクトとして任意の名称で定義し `$ref` で参照する。 -- スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Res` を付与) -- `schema` オブジェクトの `type` は `object` を指定する。 -- 異常系(`4xx`, `5xx`)の HTTP ステータスコードに対応するレスポンス定義は設計者が個別に定義するのではなく、事前に共通的なレスポンスオブジェクトを定義し `$ref` で参照することが望ましい。 - ​ +- リクエストボディそのものは通常複数の API を跨いで再利用されるものではないため、原則 `components` オブジェクトとして共通化(コンポーネント化)を行わない -```yaml -paths: - /products: - post: - operation_id: post-products - responses: - '200': - $ref: '#/components/responses/RespPostProducts' - '400': - $ref: '#/components/responses/BadRequest' - '401': - $ref: '#/components/responses/Unauthorized' - '403': - $ref: '#/components/responses/Forbidden' - '404': - $ref: '#/components/responses/NotFound' - '409': - $ref: '#/components/responses/Conflict' - '422': - $ref: '#/components/responses/UnprocessableEntity' - '500': - $ref: '#/components/responses/InternalServer' - '503': - $ref: '#/components/responses/ServiceUnavailable' - ... + - [openapi-generator](https://github.com/OpenAPITools/openapi-generator)を使用する場合は、コンポーネント化をせず、`title` を指定することで名称の指定が可能となる + - [oapi-codegen](https://github.com/oapi-codegen/oapi-codegen)を使用する場合は、名称を指定するためにコンポーネント化が必要となるが、極力コンポーネント化せずデフォルトの名称を使用することを推奨する -components: - responses: - RespPostProducts: - type: object - properties: + 良い例: + + ```yaml + paths: + /users: + post: ... - BadRequest: - ... -``` + requestBody: + required: true + content: + application/json: + ... + ``` -### paths > security + 悪い例: -APIの認証方式を記載する。 + ```yaml + paths: + /users: + get: + ... + requestBody: + # コンポーネント化したリクエストボディを参照 + $ref: '#/components/requestBodies/ReqPostProductsBody' + + components: + requestBodies: + ReqPostProductsBody: + content: + application/json: + ``` -- 通常はルートレベルの `security` でAPI共通的な認証方式を設定し、個々のAPIで個別に設定は行わない。 -- ヘルスチェックのような認証を通す必要がないAPIのみ、上書きで定義する。 +### paths > {path} > {method} > responses - 良い例; +API のレスポンスを記載する。 + +- 正常系(`2xx`)のレスポンスは通常複数の API を跨いで再利用されるものではないため、原則 `components` オブジェクトとして共通化(コンポーネント化)を行わない + + - [openapi-generator](https://github.com/OpenAPITools/openapi-generator)を使用する場合は、コンポーネント化をせず、`title` を指定することで名称の指定が可能となる + - [oapi-codegen](https://github.com/oapi-codegen/oapi-codegen)を使用する場合は、レスポンスの構造体を出力するために `strict-server` オプションを `true` に指定する必要がある。名称を指定するためにコンポーネント化が必要となるが、極力コンポーネント化せずデフォルトの名称を使用することを推奨する + + 良い例: ```yaml paths: - /signin: + /products: post: - operation_id: signin - # 認証しない場合のみ個別で定義する - security: [] + responses: + '200': + description: 200 OK + content: + application/json: + ... + ``` + + 悪い例: + + ```yaml + paths: + /products: + post: + responses: + '200': + # コンポーネント化したレスポンスオブジェクトを参照 + $ref: '#/components/responses/BadRequest' + + components: + responses: + BadRequest: + description: 200 OK + content: + application/json: + ... + ``` + + +- 異常系(`4xx`, `5xx`)のレスポンスは個別に定義するのではなく、事前に `components` オブジェクトとして定義を行い `$ref` で参照する + + 良い例: + + ```yaml + paths: + /products: + post: + responses: + '400': + # コンポーネント化したレスポンスオブジェクトを参照 + $ref: '#/components/responses/BadRequest' + + components: + responses: + BadRequest: + description: 400 Bad Request + content: + application/json: + ... + ``` + + 悪い例: + + ```yaml + paths: + /products: + post: + responses: + '400': + # レスポンスオブジェクトを個別に定義 + description: 400 Bad Request + content: + application/json: + ... + ``` + +### paths > {path} > {method} > security + +API の認証方式を記載する。 + +- 通常はルートレベルの `security` で API 共通的な認証方式を設定し、個々の API で個別に設定は行わない +- ヘルスチェックのような認証を通す必要がない API のみ、上書きで定義する + + 良い例: + + ```yaml + paths: + /session: + post: + ... + # 認証しない場合のみ個別で定義 + security: [] ``` ## components -​ -API 定義で利用する共通のデータモデルを定義 -​ +API 定義で利用する共通のデータモデルを定義する。定義方針は下記の通りである。 -```yaml -components: - schemas: ... - parameters: ... - securitySchemes: ... - requestBodies: - responses: ... - headers: ... - examples: ... - links: ... - callbacks: ... -``` +| フィールド名 | 方針 | +|-----------------|------------------------------------------------| +| schemas | API 共通的なリソース(例. ユーザや商品など)やエラー等のドメインオブジェクトを定義する | +| responses | API 共通的なレスポンス(例. 異常系(`4xx`, `5xx`)のレスポンス)を定義する | +| parameters | API 共通的なリクエストパラメータ(HTTP ヘッダやクエリパラメータ等)を定義する | +| examples | 原則何も定義しない | +| requestBodies | 原則何も定義せず、リクエストボディは API 個別に定義する | +| headers | API 共通的なレスポンスヘッダを定義する | +| securitySchemes | 標準で用いる API 認証のスキームを定義する | +| links | 原則何も定義しない | +| callbacks | 原則何も定義しない | + +※ リクエストボディやレスポンスボディにおいてオブジェクトがネストする場合、 API 固有のオブジェクトであっても `schemas` に定義する。 +これは、定義するオブジェクトの `properties` 配下に更に `type: object` が定義される場合に、生成ツールによってはうまく型が生成されないためである。 +生成ツール上問題ないのであれば、API 固有のオブジェクトを `schemas` に定義する必要はない。 +### components > schemas -### schemas +API 共通的なリソースやエラー等のドメインオブジェクトを記載する。 -- API定義共通で利用するスキーマを定義する。 -- schemasに定義する項目はリソースやエラー等のドメインオブジェクトのみとし、リクエストパラメータやレスポンスパラメータは`parameter`や`requestBodies`、`responses`に記載する。 - - `paths.requestBodies`から直接参照されるリクエストパラメータオブジェクトは`requestBodies`に定義する。 - - `paths`から直接参照されるレスポンスパラメータは`responses`に定義する。また400や500などのエラーレスポンスも`responses`に定義する。 - - HTTPヘッダやCookie、もしくは検索上限やページングのようなHTTPレイヤのパラメータに相当するものは`parameter`に定義する。 - - レスポンスヘッダーは`headers`にて定義する。 - - 上記いずれにも該当しないuserやidなどのリソース、エラーを示すオブジェクトは`schemas`に定義する。 - - 各APIのリクエストレスポンスオブジェクトは可能な限り、`parameter`,`requestBodies`,`responses`に定義する方針とし、API固有のオブジェクト(所謂`ReqXXX`、`ResXXX`等)は`schemas`には定義しない。 - - ただし、オブジェクトがネストしてしまう場合はAPI固有のオブジェクトであっても`schemas`に定義する。 - ※定義するオブジェクトの`properties`配下に更に`type: object`が定義されしまう(ネストしてしまう)と生成ツールによってはうまく型が生成されないため。 +- 名称はアッパーキャメルケースで定義する +- 名称は単数形で定義する +- `type` に複数の型を定義しない +- `type` に `null` は原則指定しない(`null` 値を用いる代わりに、キー自体を含めない) + [差分更新APIの場合](#差分更新-API-の場合)にあるとおり、空更新を行う場合は空文字を利用する +- `allOf`, `anyOf`, `oneOf` は利用しない -- 規約 - - リソース名はアッパーキャメルケースで定義する。 - - リソース名は単数形で定義する。 - - `type` に複数の型定義の指定不可。 - - `type: null`は原則として利用せず、undefinedを利用する。 - [差分更新APIの場合](#差分更新-API-の場合)にあるとおり、空更新を行う場合は空文字を利用する。 - - `allOf`、`anyOf`、`oneOf` を利用したスキーマ定義は許容しない。 +良い例: ```yaml components: schemas: - # リソースを示すオブジェクト + # 共通で使用するリソースを表すオブジェクト Product: type: object properties: @@ -442,86 +649,22 @@ components: User: type: object properties: - # エラーを示すオブジェクト + # 共通で使用するエラーを表すオブジェクト ProblemDetailError: type: object properties: ... - # リクエストパラメータやレスポンスパラメータはrequestBodies、もしくはresponsesに記載する。 - # ReqPostProductsBodyParam: - # type: object - # properties: - # ... - parameter: - # HTTPヘッダやCookie、もしくは検索上限やページングのようなHTTPレイヤのパラメータ定義 - QueryLimit: - name: limit - in: query - required: false - schema: - type: integer - description: 検索数上限 - requestBodies: - # 各API定義(paths.requestBody)から参照されるレスポンス定義 - ReqPostProductsBody: - required: true - content: - application/json: - schema: - type: object - properties: - product: - $ref: '#/components/schemas/Product' - ... - responses: - # 各API定義(paths)から参照されるレスポンス定義 - RespPostProducts: - description: 商品登録の応答 - content: - application/json: - schema: - type: object - properties: - product: - $ref: '#/components/schemas/Product' - ... - # 共通で使用するエラーレスポンス定義 - BadRequest: - description: 400 Bad Request - content: - application/json: - schema: - "$ref": "#/components/schemas/ProblemDetailError" - ``` -#### requestBodies(components) +### components > responses -- `requestBody` 直下の `required` は必須で `true` を指定する -- OpenAPI ドキュメントからソースコードを自動生成する際に生成されるのクラスや構造体の命名をコントロールしたい場合などにおいては、スキーマ定義は `component` オブジェクトとして任意の名称で定義し `$ref` で参照する。 -スキーマ定義の名称は、全体で統一された命名ルールを定めること。(例. `operation_id` をアッパーキャメルケースへ変換の上、プレフィックスに `Req` を付与) -- `schema` オブジェクトの `type` は `object` を指定する。 +API 共通的なレスポンスを記載する。主に異常系(`4xx`, `5xx`)のレスポンスを定義する。 -```yaml -components: - schemas: - Product: - type: object - properties: - ... - requestBodies: - ReqPostProductsBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Product' -``` +- 名称はアッパーキャメルケースで定義する +- 異常系(`4xx`, `5xx`)のレスポンスの場合、名称にステータスコードの名称(例. BadRequest, Unauthorized)を用いる -#### responses(components) +良い例: -レスポンスの先頭には複数のエンドポイントで横断的に用いるモデルを定義する。例えば、ステータスコード400~500系のエラーモデルがある。 -​ ```yaml components: schemas: @@ -530,12 +673,14 @@ components: properties: ... responses: + # HTTP ステータスコード 400 のレスポンスオブジェクト BadRequest: description: 400 Bad Request content: application/json: schema: "$ref": "#/components/schemas/ProblemDetailError" + # HTTP ステータスコード 401 のレスポンスオブジェクト Unauthorized: description: 401 Unauthorized content: @@ -544,8 +689,11 @@ components: "$ref": "#/components/schemas/ProblemDetailError" ... ``` -​ -正常系のレスポンスの例としてはファイルアップロード・ダウンロードなどが該当する。個別のアプリケーション要件でブレが少ないと複数のエンドポイントで用いられる場合に定義する。オブジェクトのスキーマは、schemasに切り出して定義し、コード生成ツールのために型情報を付与させる。 + +正常系のレスポンスの例としてはファイルアップロード・ダウンロードのレスポンスなどが該当する。 +個別のアプリケーション要件でブレが少なく、複数のエンドポイントで用いられる場合に定義する。オブジェクトのスキーマは、`schemas` に切り出して定義し、コード生成ツールのために型情報を付与させる。 + +良い例: ```yaml components: @@ -581,47 +729,21 @@ components: format: binary ``` -それらの後に、paths登場順にエンドポイント固有のレスポンスを定義する。レスポンスオブジェクトのスキーマは、schemasに切り出して定義する。 +### components > parameters -```yaml -components: - schemas: - Product: - type: object - properties: - product_id: - type: string - ... - responses: - ... - RespPostProducts: - description: 商品登録の応答 - content: - application/json: - schema: - "$ref": "#/components/schemas/Product" - examples: - default: - value: - ... - example-1: - $ref: './examples/post-product.example.1.yaml' -``` - -#### parameters - -API 共通で利用するパラメータ(パスパラメータ、クエリパラメータ、ヘッダ, Cookie)を記載する。 +API 共通的なリクエストパラメータ(パスパラメータ、クエリパラメータ、ヘッダ, Cookie)を記載する。 -##### パスパラメータ +#### パスパラメータ -- API 全体で利用されるパスパラメータが必要なケースが想定されないため、原則定義しない。 -特定リソースの操作(例えば更新と削除)を行う際のリソースIDはパスパラメータとして再利用できるが、コンフリクトを避けるため原則共通化は行わない。 +- API 全体で利用されるパスパラメータが必要なケースが想定されないため、原則定義しない + 特定リソースの操作(例えば更新と削除)を行う際のリソース ID はパスパラメータとして再利用できるが、コンフリクトを避けるため原則共通化は行わない -##### クエリパラメータ +#### クエリパラメータ -- API 全体で利用可能な共通のクエリパラメータを定義する (例: 検索数のlimit, offset) -- 命名は クエリパラメータ名に `Query` というプレフィクスを付与する形式を推奨する。 +- API 全体で利用可能な共通のクエリパラメータ(例: 検索数の limit, offset)を定義する +- 命名はクエリパラメータ名に `Query` というプレフィックスを付与する形式を推奨する +良い例: ```yaml paths: @@ -630,20 +752,23 @@ paths: parameters: - $ref: '#/components/parameters/QueryLimit' -parameters: - QueryLimit: - name: limit - in: query - required: false - schema: - type: integer - description: 検索数上限 +components: + parameters: + QueryLimit: + name: limit + in: query + required: false + schema: + type: integer + description: 検索数上限 ``` -##### ヘッダパラメータ +#### ヘッダ + +- API 全体で利用可能な共通のリクエストヘッダを定義する +- 命名はヘッダ名に `Header` というプレフィックスを付与する形式を推奨する -- API 全体で利用可能な共通のリクエストヘッダを定義する。 -- 命名は ヘッダ名に `Header` というプレフィクスを付与する形式を推奨する。 +良い例: ```yaml paths: @@ -651,7 +776,7 @@ paths: /products: parameters: - $ref: '#/components/parameters/HeaderContentType' -... + components: parameters: HeaderContentType: @@ -662,11 +787,13 @@ components: required: true ``` -##### Cookie パラメータ +#### Cookie + +- API 全体で利用可能な共通の Cookie(例: CSRF 用のトークン)を定義する +- 命名は Cookie 名に `Cookie` というプレフィックスを付与する形式を推奨する +- Cookie 認証を定義する場合は、`APIKey` を利用する -- API 全体で利用可能な共通のCookieパラメータを定義する。(例: CSRF用のトークン) -- 命名は Cookie パラメータ名に `Cookie` というプレフィクスを付与する形式を推奨する。 -- Cookie 認証を定義する場合は、`APIKey` を利用すること。 +良い例: ```yaml paths: @@ -674,7 +801,7 @@ paths: /products: parameters: - $ref: '#/components/parameters/CookieCSRFToken' -... + components: parameters: CookieCSRFToken: @@ -686,11 +813,17 @@ components: description: CSRFトークン ``` -### headers +### components > requestBodies + +原則何も定義せず、リクエストボディは API 個別に記載する。 -API 共通で利用するレスポンスヘッダを記載する。 +### components > headers -- 命名は ヘッダ名からハイフンを除去した形式を推奨する。 +API 共通的なレスポンスヘッダを記載する。 + +- 命名はヘッダ名からハイフンを除去した形式を推奨する + +良い例: ```yaml paths: @@ -699,21 +832,22 @@ paths: responses: '200': headers: - ContentType: - $ref: '#/components/headers/ContentType' -... + XCacheInfo: + $ref: '#/components/headers/XCacheInfo' components: headers: - ContentType: - description: the original media type of the resource + XCacheInfo: + description: not cacheable; meta data too large schema: type: string ``` -### securitySchemes +### components > securitySchemes + +標準で用いる API 認証の定義を行う。 -標準で用いるAPI認証の定義を行う。 +良い例: ```yaml components: @@ -726,38 +860,38 @@ components: description: 'Bearer トークン認証' ``` -### links +### components > links -[links](https://swagger.io/docs/specification/links/) は OpenAPI 3.0 の新機能の1つで、あるAPIレスポンスの値を用いて、別のAPIを呼び出す方法を明示できるセクションである。 +[links](https://swagger.io/docs/specification/links/) は OpenAPI 3.0 の新機能の1つで、ある API レスポンスの値を用いて、別の API を呼び出す方法を明示できるセクションである。 -興味深い機能であり、APIのセマンティクスを伝えるのに有用であるが、本規約では記載しないことを推奨とする。 +興味深い機能であり、API のセマンティクスを伝えるのに有用であるが、本規約では記載しないことを推奨とする。 理由は下記の通りである。 -- 業務システムでは、業務フローを抑えておけば、API操作フローの理解はそこまで難しくないことが多い - - 逆に、API同士の関係だけを示すだけでは業務モデリング図とのダブルメンテナンスになったり、中途半端になりうる -- [OAS 3.0 Support Backlog](https://github.com/swagger-api/swagger-ui/issues/3641) にあるように、2023/12/15時点ではSwagger-UIが対応していない - - linksを書いたと言って、APIドキュメントに影響しない +- 業務システムでは、業務フローを抑えておけば、API 操作フローの理解はそこまで難しくないことが多い + - 逆に、API 同士の関係だけを示すだけでは業務モデリング図とのダブルメンテナンスになったり、中途半端になりうる +- [OAS 3.0 Support Backlog](https://github.com/swagger-api/swagger-ui/issues/3641) にあるように、2023/12/15時点では Swagger-UI が対応していない + - links を書いたと言って、API ドキュメントに影響しない -### callbacks +### components > callbacks -[callbacks](https://swagger.io/docs/specification/callbacks/) は OpenAPI 3.0 の新機能の1つで、APIサーバ側が指定されたコールバックURLを呼び出すという仕組みである。 +[callbacks](https://swagger.io/docs/specification/callbacks/) は OpenAPI 3.0 の新機能の1つで、API サーバ側が指定されたコールバック URL を呼び出すという仕組みである。 -仕様書には、ECショップで購入のたびにマネージャーに通知を送るといった、何かしらの処理をトリガーにコールバックURLを呼び出す例が示されている。 +仕様書には、EC ショップで購入のたびにマネージャーに通知を送るといった、何かしらの処理をトリガーにコールバック URL を呼び出す例が示されている。 利便性は高い仕様だが、本規約では記載しないことを推奨とする。 理由は下記の通りである。 -- コールバックURL呼び出しの、エラーハンドリングが難しい +- コールバック URL 呼び出しの、エラーハンドリングが難しい - 業務システムでは欠損が許されない、または将来的に許されなくなる可能性があり、その場合にこの機能に頼ると想定以上の追加作業が発生する コールバックのような仕組みを実現するには、別途キューイングのメッセージサービスの利用などを検討する。 ## security -全APIに共通で適用されるセキュリティ設定を定義する。 -業務システムの Web API において 認証が全く存在しないケースは考えにくいため、本規約ではルートレベルで認証を設定し、個々のAPIへの適応漏れをなくす。 +全 API に共通で適用されるセキュリティ設定を定義する。 +業務システムの Web API において認証が全く存在しないケースは考えにくいため、本規約ではルートレベルで認証を設定し、個々の API への適応漏れを無くす。 良い例: @@ -768,12 +902,13 @@ security: ## tags -API を論理的にグループ化するためのタグを定義する。ドキュメントやツールにとって重要であるため、 **必須** で指定する。 +API を論理的にグループ化するためのタグを定義する。 -- `name`, `description` を必須項目とする。 -- **単数形** で、小文字かつ半角スペース区切りで記載する。 - 半角スペース区切りで記載する理由は HTML ドキュメントで参照する場合の可読性を上げるためである。 -- コード生成で利用される(Go においてはパッケージ、 TypeScriptにおいてはクラスに相当する)ため、シンプルな命名にする。 +- ドキュメントやツールにとって重要であるため **必須** で指定する +- `name`, `description` を必須項目とする +- **単数形** で、小文字かつ半角スペース区切りで記載する + 半角スペース区切りで記載する理由は HTML ドキュメントで参照する場合の可読性を上げるためである +- コード生成で利用される(Go においてはパッケージ、 TypeScript においてはクラスに相当する)ため、シンプルな命名にする 良い例: @@ -797,9 +932,8 @@ tags: ## externalDocs -参照情報としてのURLの記載が可能。 -ただし、`description` にて参考情報となるURLを記載する方が、複数リンクを指定可能であるなど自由度が高く使いやすい。そのため `externalDocs` は利用せず `description` の利用を推奨する。 - +参照情報としての URL を記載できる。 +ただし、`description` にて参考情報となる URL を記載する方が、複数リンクを指定可能であるなど自由度が高く使いやすいため `externalDocs` は利用せず `description` の利用を推奨する。 良い例: @@ -816,19 +950,17 @@ externalDocs: url: http://swagger.io ``` -悪い例: - -```yaml -externalDocs: - description: Find out more about Swagger - url: http://swagger.io -``` - # 設計上のポイント +OpenAPI ドキュメントを作成する上での設計上ポイントをいくつか記載する。 +一部の内容については、 + ## ファイルアップロード -Web API におけるファイルアップロードのよく利用される実装手段は、大きく分けて以下の 3 手法に分類できます +
+詳細をみる + +Web API におけるファイルアップロードのよく利用される実装手段は、大きく分けて以下の 3 手法に分類できる。 1. ファイルのコンテンツを Base64 などにエンコードして、JSON の項目として設定し、リクエストボディで送る - メリット: 通常の JSON を扱うのとほぼ変わらないため楽。サムネイルなど限定されたユースケースの場合に向く @@ -841,7 +973,7 @@ Web API におけるファイルアップロードのよく利用される実装 - Amazon API Gateway を利用する場合は、2023 年 6 月時点で[ペイロード上限が 10MB](https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html)、[AWS Lambda でもペイロード制限がある](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/gettingstarted-limits.html#api-requests)ため、許容するファイルサイズによってはこの手法一択となる - メリット: オブジェクトストレージの可用性・信頼性を享受できる - デメリット: アップロードするために複数の API エンドポイント呼び出しが必要なため、煩雑である - - 2023 年 6 月に AWS ブログでこの方式について解説した記事が出たので、詳細は参照ください。 + - 2023 年 6 月に AWS ブログでこの方式について解説した記事が出たので、詳細は参照ください - [https://aws.amazon.com/jp/blogs/news/large-size-files-transferring-by-serverless-s3presignedurl-and-clientside-javascript/](https://aws.amazon.com/jp/blogs/news/large-size-files-transferring-by-serverless-s3presignedurl-and-clientside-javascript/) 本規約でファイルアップロードについて上記の 3. Signed URL を推奨する。API 呼び出しとしては次のようなフローとする。 @@ -855,7 +987,7 @@ participant C as オブジェクトストレージ A->>B: ①アップロード先URL取得 B->>C: Signed URL発行 C-->>B: Signed URL - B-->>A: アップロードURL、受付ID(加えて、アップロードで指定したいHTTP Methodや必要なリクエストヘッダーがあれば応答の項目に追加する) + B-->>A: アップロードURL、受付ID(加えて、アップロードで指定したいHTTP Methodや必要なリクエストヘッダがあれば応答の項目に追加する) A->>C: ②ファイルアップロード @@ -867,16 +999,26 @@ A->>B: ③ファイルアップロード完了(受付ID、キー、属性) 上記どちらのケースも OpenAPI 定義としてはシンプルであるため、記述例は割愛する。 +
+ ## ファイルダウンロード +
+詳細をみる + ファイルアップロードと同様、オブジェクトストレージの Signed URL 経由を経由してのダウンロードさせる手法を推奨する。Web API としてはオブジェクトストレージにダウンロード用のファイルを書き込み、クライアントが取得するための Signed URL をレスポンスの JSON 項目に渡す方式である。 もし、サムネイルやアイコン画像など、ファイル容量がごく小さい場合は Base64 にエンコードして JSON に埋め込んで渡しても良い。線引をどこに設置するかは本規約で定義しない。 どちらのケースも OpenAPI 定義としてはシンプルであるため、記述例は割愛する。 +
+ ## CORS +
+詳細をみる + CORS(Cross-Origin Resource Sharing)のために、options メソッドの追記は **原則不要** とする。 理由は以下である。 @@ -892,18 +1034,28 @@ CORS(Cross-Origin Resource Sharing)のために、options メソッドの追 [^1]: https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/enable-cors-for-resource-using-swagger-importer-tool.html +
+ ## OpenTelemetry Traceparent HTTP Header -OpenOpenTelemetryで用いるられる[traceparent](https://www.w3.org/TR/trace-context/) のリクエストヘッダーはOpenAPIで **原則不要** とする。 +
+詳細をみる + +OpenOpenTelemetry で用いるられる[traceparent](https://www.w3.org/TR/trace-context/) のリクエストヘッダは OpenAPI で **原則不要** とする。 理由は以下である。 -- OpenTelemetryが定めるヘッダー類は、API横断的に設定されるべきものであり、ミドルウェアやフレームワーク側などでの一律の制御を推奨するため -- 記載することにより、OpenOpenTelemetryに対応していることを明記し開発者に周知できるメリットより、各アプリ開発者が生成されたコードで悩んだり、誤解されることを回避したいため +- OpenTelemetry が定めるヘッダ類は、API 横断的に設定されるべきものであり、ミドルウェアやフレームワーク側などでの一律の制御を推奨するため +- 記載することにより、OpenOpenTelemetry に対応していることを明記し開発者に周知できるメリットより、各アプリ開発者が生成されたコードで悩んだり、誤解されることを回避したいため + +
-## バリデーションについて +## バリデーション -OpenAPI 定義を記載するにあたり、バリデーションをどこまで厳密に定義すべきかという議論はよく行いがちである。 +
+詳細をみる + +パラメータのバリデーションをどこまで厳密に定義すべきかという議論はしばしば行われる。 リクエストパラメータの各項目に対して、必須・型・桁・区分値・日付・正規表現のチェックが行える。レスポンスで用いるモデルについても同様に設定でき、`enum`, `pattern` 以外は API の利用者(クライアント)側の DB 設計などに必要な型桁情報を渡すのに有用であるため、できる限り詳しく指定する。 @@ -928,26 +1080,25 @@ description: 検索結果の項目数上限(1~100が指定可能) 【注意】API 公開後に、default 値を変更してはならない(API の互換性が崩れるため)。もし変更する場合は、API のバージョンを上げること。 - ### 型・フォーマット -型(`type`)は `string(文字列)`, `number(数値)`, `integer(整数値)`, `boolean(真偽値)` `array(配列)` のうちどれか指定する. +型(`type`)は `string(文字列)`, `number(数値)`, `integer(整数値)`, `boolean(真偽値)` `array(配列)` のうちどれか指定する。 -フォーマット(`format`) は以下の型の詳細情報を示すもので、可能な限り設定する。 +フォーマット(`format`)は以下の型の詳細情報を示すもので、可能な限り設定する。 -- `integer` (整数) +- `integer`(整数) - `int32`, `int64` -- `number` (数値) +- `number`(数値) - `float`, `double` -- `string` (バイナリ) +- `string`(バイナリ) - `byte`: Base64 でエンコードされた文字列 - `binary`: バイト配列 -- `string` (日付) +- `string`(日付) - `date`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) full-date(例: 2023-07-21) - 項目名は `_on` を接尾辞につけることを推奨とする - `date-time`: [RFC3339](https://www.rfc-editor.org/rfc/rfc3339) date-time(例: 2023-07-21T17:32:28Z) - 項目名は `_at` を接尾辞につけることを推奨とする -- `string` (その他) +- `string`(その他) - `password`: Swagger UI で入力が隠される - その他、 `email`, `uuid` など Open API 仕様に存在しない任意のフォーマットを独自のドキュメント生成などのために記載しても良い @@ -993,7 +1144,7 @@ description: | 9: 適用不能 ``` -OpenAPI 3.0 では区分値の再利用ができるため、横断的に用いる区分値はcomponents側で定義する。 +OpenAPI 3.0 では区分値の再利用ができるため、横断的に用いる区分値は components 側で定義する。 ```yaml paths: @@ -1037,354 +1188,45 @@ remind_time: pattern: "^(2[0-3]|[01][0-9]):([0-5][0-9])$" ``` -## 値が存在しないという状態の表現 - -### undefined と null - -- リクエスト/レスポンスにおいて、ある項目の値が存在しないという状態を表現する場合、① その項目自体を含めず `undefined` とする方法と、② 値に `null` を使用する方法がある。 - - ① `undefined` とする場合、OpenAPI 定義と JSON データの例 - - ```yaml - application/json: - schema: - type: object - properties: - id: - type: string - name: - type: string - required: - - id - ``` - - ```jsonc - { "id": "00001" } //nameの値が存在しない - ``` - - ```jsonc - {} //idの値も存在しない場合、idは項目必須であるためバリデーションエラーとなる - ``` - - ② 値に `null` を使用する場合、OpenAPI 定義(OpenAPI3.0)と JSON データの例 - - ```yaml - application/json: - schema: - type: object - properties: - id: - type: string - nullable: true - name: - type: string - nullable: true - required: - - id - ``` - - ```jsonc - { "id": "00001", "name": null } //nameの値が存在しない - ``` - - ```jsonc - { "id": null, "name": null } //id, nameとも値が存在しない - ``` - - ```jsonc - {} //この場合は、required指定の、id項目がないためバリデーションエラーとなる - ``` - -- 原則としては、①`undefined` による定義を使用する方が、API 仕様の表現が煩雑にならず、また通信サイズの点からも有利である。 - -### 差分更新 API の場合 - -- 差分更新(PATCH)API においては、項目が更新対象外であることと、項目が更新してクリアする対象であることを明確に区別する必要がある。このような場合には、以下のいずれかの方法を採用する。 - - 1)項目を文字列として定義し、空値として空文字("")を採用する。 - - - float や int 等の数値についても、文字列型として定義することで、項目が `undefined` または `null` であれば「更新対象外」、項目が空文字であれば「更新してクリアする対象」であると判定できる。 - - この場合、空文字は OpenAPI の`format`定義に許容されないため注意が必要である。`format`定義の代わりに、`pattern`に空文字を許容する正規表現を定義する必要がある。 -
- - 2)カスタムモデルを使用する +
- - API で受け取るデータをデシリアライズする際に、`undefined` と `null` を区別できるようなカスタムモデルを作成すると、1)のように数値を文字列型として扱う必要がなく、OpenAPI の`format` 定義を使用することも可能である。 - - この場合、OpenAPI からの自動生成モデルにはカスタマイズが必要となる。 - - カスタムモデルの例 (参照元: [技術ブログ](https://future-architect.github.io/articles/20211028b/#プログラムの観点)) - -### 各言語による表現の違い - -
- Golang - - - Golangの場合、以下のように、`nil` になり得る項目はポインタ型とした上で、`undefined` となり得る項目は `omitempty` タグを付与する。 - - ```go - type Data struct { - NotNullableString1 string `json:"not_nullable_string_1"` - NotNullableString2 string `json:"not_nullable_string_2"` - NullableString1 *string `json:"nullable_string_1"` - NullableString2 *string `json:"nullable_string_2,omitempty"` - NullableString3 *string `json:"nullable_string_3"` - NullableString4 *string `json:"nullable_string_4,omitempty"` - NotNullableInt1 int64 `json:"not_nullable_int_1"` - NotNullableInt2 int64 `json:"not_nullable_int_2"` - NullableInt1 *int64 `json:"nullable_int_1"` - NullableInt2 *int64 `json:"nullable_int_2,omitempty"` - NullableInt3 *int64 `json:"nullable_int_3"` - NullableInt4 *int64 `json:"nullable_int_4,omitempty"` - } - - // Set values - data := Data{ - NotNullableString1: "value", - NullableString1: nil, - NullableString2: nil, - NotNullableInt1: 1, - NullableInt1: nil, - NullableInt2: nil, - } - // Serialize - jsonString, _ := json.Marshal(data) - fmt.Println(string(jsonString)) - buf := bytes.NewBuffer(jsonString) - - decoder := json.NewDecoder(buf) - var v Data - // Deserialize - decoder.Decode(&v) - fmt.Printf("After decoded: %#v\n", v) - ``` - - - シリアライズ後のjsonを見ると、値がセットされない場合には、項目にゼロ値(ポインタ型は`nil`, string型は空文字、int型は`0`)が入っている。 - - 項目がゼロ値の場合に`omitempty` が付与されていると、 項目ごと除外されている(`undefined` となっている)。 - - ```json - { - "not_nullable_string_1": "value", - "not_nullable_string_2": "", - "nullable_string_1": null, - "nullable_string_3": null, - "not_nullable_int_1": 1, - "not_nullable_int_2": 0, - "nullable_int_1": null, - "nullable_int_3": null - } - ``` - - - デシリアライズ後の構造体を見ると、json の項目が`undefined`であっても `null` であっても、`nil` として保持されている。 - - ```go - After decoded: - Data{ - NotNullableString1:"value", - NotNullableString2:"", - NullableString1:(*string)(nil), - NullableString2:(*string)(nil), - NullableString3:(*string)(nil), - NullableString4:(*string)(nil), - NotNullableInt1:1, - NotNullableInt2:0, - NullableInt1:(*int64)(nil), - NullableInt2:(*int64)(nil), - NullableInt3:(*int64)(nil), - NullableInt4:(*int64)(nil) - } - ``` - -
- -
- Java - - - Java の場合、`int` や `double` などのプリミティブ型は `null` になれないため、`nullable` にするためには、それぞれのラッパークラスである参照型(`Integer`, `Double` など)を使用する必要がある。 - - json にシリアライズ後に`null` の項目を保持するか否かは、例えば、[Jackson ライブラリ](https://github.com/FasterXML)を用いて以下のように区別される。 - - ```java - public class Data { - public Data(){}; - public Data(String str1, String str2, int notNullableInt){ - this.nullableString1 = str1; - this.nullableString2 = str2; - this.notNullableInt = notNullableInt; - }; - @JsonInclude(JsonInclude.Include.ALWAYS) - private String nullableString1; - - @JsonInclude(JsonInclude.Include.NON_NULL) - private String nullableString2; - - private int notNullableInt; - - // Setters - public void setNullableString1(String nullableString1) { - this.nullableString1 = nullableString1; - } - public void setNullableString2(String nullableString2) { - this.nullableString2 = nullableString2; - } - public void setNotNullableInt(int notNullableInt) { - this.notNullableInt = notNullableInt; - } - // Getters - public String getNullableString1() { - return nullableString1; - } - public String getNullableString2() { - return nullableString2; - } - public int getNotNullableInt() { - return notNullableInt; - } - } - ``` - - ```java - // Set nothing to the fields. - Data dataWithNothing = new Data(); - // Set intial values to the fields. - Data dataWithInitialValues = new Data(null,null,0); - // Set values to the fields. - Data dataWithValues = new Data("","",1); - - List dataList = Arrays.asList(dataWithNothing, dataWithInitialValues, dataWithValues); - ObjectMapper mapper = new ObjectMapper(); - for(Data d : dataList){ - // Serialize - String json = mapper.writeValueAsString(d); - System.out.println(json); - - // Deserialize - Data deserialized = mapper.readValue(json, Data.class); - System.out.println(ToStringBuilder.reflectionToString(deserialized, ToStringStyle.SHORT_PREFIX_STYLE)); - } - ``` - - - シリアライズ後の json を見ると、参照型`String`の初期値は`null`、プリミティブ型`int`の初期値は`0`となっている。 - - `@JsonInclude(JsonInclude.Include.ALWAYS)` アノテーションを付与した項目は、値が`null`の場合でも項目が保持される。 - - `@JsonInclude(JsonInclude.Include.NON_NULL)` アノテーションを付与した項目は、値が`null`の場合には項目ごと除外されている(`undefined`となっている)。 - - ```json - { - "nullableString1": null, - "notNullableInt": 0 - } - - { - "nullableString1": null, - "notNullableInt": 0 - } - - { - "nullableString1": "", - "nullableString2": "", - "notNullableInt": 1 - } - ``` - - - デシリアライズ後のオブジェクトを見ると、json の項目が`undefined`であっても `null` であっても、`null` として保持されている。 - - ```java - Data[nullableString1=,nullableString2=,notNullableInt=0] - - Data[nullableString1=,nullableString2=,notNullableInt=0] - - Data[nullableString1=,nullableString2=,notNullableInt=1] - ``` - -
- -
- TypeScript - - - TypeScriptの場合、以下のように、`null`, `undefined` を区別して定義することが可能である。Optional指定(項目名に`?`を付与)することで`undefined`をセットする必要がなくなる(項目が「非必須」となる)。 - - ```typescript - interface Data { - nullable_string1: string; - nullable_string2: string; - nullable_string3: string; - nullable_string4: string; - nullable_string5?: string; - nullable_num1: number; - nullable_num2: number; - nullable_num3: number; - nullable_num4: number; - nullable_num5?: number; - } - - const body: Data = { - nullable_string1: 'value1', - nullable_string2: '', - nullable_string3: null, - nullable_string4: undefined, // 定義しないとエラーとなる - nullable_num1: 1, - nullable_num2: 0, - nullable_num3: null, - nullable_num4: undefined, // 定義しないとエラーとなる - } - var jsonString = JSON.stringify(body) - console.log(jsonString) - - const deserialized: Data = JSON.parse(jsonString); - console.log(deserialized) - ``` - - - シリアライズ後のjsonを見ると、`undefined`定義した項目は除外されている。 - - ```json - { - "nullable_string1": "value1", - "nullable_string2": "", - "nullable_string3": null, - "nullable_num1": 1, - "nullable_num2": 0, - "nullable_num3": null - } - ``` +## 値が存在しないという状態の表現 - - デシリアライズ後のオブジェクトを見ると、json の項目が`null` の場合にのみ`null` として保持されており、項目のない場合と区別されている。 +
+詳細をみる - ```typescript - nullable_string1: "value1"; - nullable_string2: ""; - nullable_string3: null; - nullable_num1: 1; - nullable_num2: 0; - nullable_num3: null; - ``` -
+原則 `null` を用いず、パラメータのキー自体を含めないこと(`undefined`)による表現を行う。 +詳細は[技術ブログ記事](https://future-architect.github.io/articles/20211028b/)を参照されたい -### 参照リンク +
-- `undefined` と `null` の使い方について詳細な解説は、[技術ブログ記事](https://future-architect.github.io/articles/20211028b/)を参照されたい。 -- OpenAPI 定義を DB 定義に対応させることにより、異なる API 間で整合のとれた処理設計をすることがのぞましい。DB 定義と OpenAPI 定義の対応例は、[DB 定義と OpenAPI 定義のマッピング](./reference/DB_OpenAPI_Mapping_Example.md)を参照されたい。 +## ファイル分割 -## ファイル単位 +
+詳細をみる OpenAPI ドキュメントは単一のファイルで構成することも複数の分割されたファイルで構成することもできるが、**複数のファイルに分割する**ことを推奨する。 理由は下記の通りである。 -- **APIごとに担当者を分けて設計する場合などに、複数人による編集によって意図しないコンフリクトが発生することを防ぐ。** -- **ファイルの肥大化による、可読性の低下を防ぐ。** +- API ごとに担当者を分けて設計する場合などに、複数人による編集によって意図しないコンフリクトが発生することを防ぐ +- ファイルの肥大化による、可読性の低下を防ぐ ### 分割方法の選定 -開発方針やOpenAPIの使用用途に合わせて、都合の良いファイルの分割方法を採用する。例えば、以下のような方法がある。 +開発方針や OpenAPI の使用用途に合わせて、都合の良いファイルの分割方法を採用する。例えば、以下のような方法がある。 -1. APIごとに設計担当者を分けて、それぞれにOpenAPIを編集する場合は、APIの単位で分割する。 -2. テストツールとして [stoplightio/prism](https://github.com/stoplightio/prism)を使用する場合、テストケースごとにデータファイルを作成して、`examples` にファイルパスを指定する。 +1. API ごとに設計担当者を分けて、それぞれに OpenAPI を編集する場合は、API の単位で分割する +2. テストツールとして [stoplightio/prism](https://github.com/stoplightio/prism)を使用する場合、テストケースごとにデータファイルを作成して、`examples` にファイルパスを指定する ### サンプル説明 分割方法1, 2の両方に当てはまる場合のサンプルを用いて説明する。`openapi.yaml` とディレクトリ構成は下の通り。サンプルの全量は [サンプルzip Download](./reference/divided_files_sample.zip)からダウンロード可能。 -- 機能単位(path, method単位)にディレクトリを作成して、それぞれの定義ファイルを格納する。ディレクトリ名は `{path}_{method}` とすると管理し易い。 +- 機能単位(path, method 単位)にディレクトリを作成して、それぞれの定義ファイルを格納する。ディレクトリ名は `{path}_{method}` とすると管理し易い - `components` の `schemas` には、 - - API間で同じモデルを使用する場合は共通化して記載する(例えば、`Pet`)。 - - 各APIのリクエスト/リスポンスモデルの中で、モデルがネストする場合は、各モデルの単位で書き出す(例えば、`PetDetail`, `Pedigree`)。 - - ※schemasのモデルの中身は別ファイルに定義が可能だが、大本のopenapi.yamlにも命名のみ定義が必要。openapi.yamlの定義が無いとswaggerUIで確認した際にschemas定義が見えなくなってしまう。 + - API 間で同じモデルを使用する場合は共通化して記載する(例えば、`Pet`) + - 各 API のリクエスト/リスポンスモデルの中で、モデルがネストする場合は、各モデルの単位で書き出す(例えば、`PetDetail`, `Pedigree`) + - ※schemas のモデルの中身は別ファイルに定義が可能だが、大本の openapi.yaml にも命名のみ定義が必要。openapi.yaml の定義が無いと swaggerUI で確認した際に schemas 定義が見えなくなってしまう
ファイル分割例: openapi.yaml @@ -1454,9 +1296,9 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数
-- `openapi.yaml` の `paths` に記載したAPIファイルは以下のように作成する。 -- 複数API間に共通のモデルについては `openapi.yaml` に指定したキー(`../openapi.yaml#/components/schemas/Pet`)を参照する。 -- `examples` には、各APIのテストケースIDをキーとして指定(`ResExample1`)し、`value` に該当するテストケースのデータファイルパスを指定(`./examples/res_example1.yaml`)する。ファイル名は、指定したキーをスネークケースに変換したものを使用するとよい。 +- `openapi.yaml` の `paths` に記載した API ファイルは以下のように作成する +- 複数 API 間に共通のモデルについては `openapi.yaml` に指定したキー(`../openapi.yaml#/components/schemas/Pet`)を参照する +- `examples` には、各 API のテストケース ID をキーとして指定(`ResExample1`)し、`value` に該当するテストケースのデータファイルパスを指定(`./examples/res_example1.yaml`)する。ファイル名は、指定したキーをスネークケースに変換したものを使用するとよい
API別ファイルの記載例: pets-pet-id_get.yaml @@ -1540,7 +1382,7 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数
-- OpenAPIの使用用途により、分割ファイルを1つのファイルにまとめる必要がある場合には、例えば[swagger-cli](https://apitools.dev/swagger-cli/)を使用して以下コマンドを実行する。 +- OpenAPI の使用用途により、分割ファイルを1つのファイルにまとめる必要がある場合には、例えば[swagger-cli](https://apitools.dev/swagger-cli/)を使用して以下コマンドを実行する ```bash swagger-cli bundle openapi.yaml --outfile openapi.gen.yaml --type yaml @@ -1809,6 +1651,7 @@ OpenAPI ドキュメントは単一のファイルで構成することも複数
+ --- diff --git a/documents/forOpenAPISpecification/README.md b/documents/forOpenAPISpecification/README.md index b752eb34..c07ef6de 100644 --- a/documents/forOpenAPISpecification/README.md +++ b/documents/forOpenAPISpecification/README.md @@ -36,11 +36,11 @@ OAS は次のように複数のバージョンが存在します。 [https://openapi.tools/](https://openapi.tools/) -# OpenAPI Specification Standards 規約 +# OpenAPI Specification Standards -| Version | コーディング規約 | -| ------- | ---------------------------------------------------------- | -| 3.0.3 | OAS 3.0.3 規約(作成中) | +| Version | コーディング規約 | +|---------|--------------------------------------------------------| +| 3.0.3 | [OAS 3.0.3 規約](OpenAPI_Specification_3.0.3.md) | | 2.0 | [OAS 2.0(Swagger 2.0)規約](OpenAPI_Specification_2.0.md) | # Resources