OpenAPI Specification 3.0.3規約

フューチャー株式会社

本コーディング規約は、世の中のシステム開発プロジェクトのために無償で提供致します。
ただし、掲載内容および利用に際して発生した問題、それに伴う損害については、フューチャー株式会社は一切の責務を負わないものとします。
また、掲載している情報は予告なく変更することがございますので、あらかじめご了承下さい。

# はじめに

本ドキュメントは OpenAPI Specification 3.0.3 (opens new window)に則った API ドキュメントを記述する際のコーディング規約をまとめている。 旧バージョンであるOpenAPI Specification 2.0 の規約も存在するため、v2を使用している場合はそちらを参照されたい。

本規約は前提条件に基づいて作成されており、ToC 向けの LSUDs(Large Set of Unknown Developers)向けの Web API には適合しない場合もあるのでご留意いただきたい。 Web API の設計自体はこの規約の範囲外であるが、API 設計標準 にステータスコード等の標準を記載しているため、必要に応じて参考にされたい。

# ファイルフォーマット

ファイルフォーマット規約に従う。

# OpenAPI ドキュメントの構成要素

OpenAPI ドキュメントを構成する要素はオブジェクトと呼ばれ、ルートオブジェクトは以下の要素で構成される。
各種規約を理解する上で、これらの要素を大まかに把握しておくことが重要である。
各オブジェクトの詳細については公式ドキュメント (opens new window)を参照されたい。

フィールド名 必須 説明
openapi OpenAPI ドキュメントが使用する OpenAPI 仕様のバージョン番号
info API に関するメタデータ
servers API サーバへの接続情報
paths API の利用可能なパスと操作方法
components 複数の API における共通の定義
security API 全体で利用可能なセキュリティ(認証)機構
tags 各種 API をグルーピングするためのタグ
externalDocs 追加の外部ドキュメント

# 要素規約

先述した OpenAPI ドキュメントを構成する要素別に具体的なコーディング規約を記載する。

# openapi

OpenAPI ドキュメントが使用する OpenAPI 仕様のセマンティックバージョン番号を記載する。
本規約はバージョン3.0.3を対象としているため、3.0.3とする。

良い例:

openapi: 3.0.3
1

悪い例:

openapi: 3.0
1

# info

infoオブジェクトには Web API に関するメタデータを記載する。
title, description, version を必須項目とする。

フィールド名 必須 記載内容
title Web API の総称
description Web API の簡単な説明
version OpenAPI ドキュメントのバージョン
termsOfService 利用規約の URL
contact 連絡先情報
license ライセンス情報

# info > title

Web API の総称を記載する。
システム名やサービス名 + API のような命名を推奨する。

良い例:

info:
  title: X System API
1
2

# info > description

Web API が提供する機能の概要・想定する利用者やユースケース・制約などを記載する。

# info > version

この API 仕様のドキュメントのバージョンを記載する。
アプリケーションのバージョン(git tag やリリースで管理するようなバージョン)とは別である。

  • major.minor 形式を推奨する
    0.1固定で開発を進め、サービスのリリース時に 1.0 とし、その後の項目やオプション、パスの追加ごとにマイナーバージョンをインクリメントしていく

    良い例:

    info:
      version: 1.0
    
    1
    2
  • 他チームへの API ドキュメントの頻繁な共有が必要な場合は YYYY.MM.DD の日付形式も許容する

    良い例:

    info:
      version: 2023.03.26
    
    1
    2

# servers

Web API を提供するサーバの情報を記載する。

  • url, description を必須項目とする
  • ステージ(local, develop, staging など)が複数ある場合は各ステージ分の情報を記載する。
  • SSKDs 向けの Web API 開発においては本番環境の URL を不用意に公開したくないケースが多く、記載は避けるべきである

良い例:

servers:
  - url: http://localhost:8001/
    description: Localhost Server
  - url: https://dev.api.example.com/v1
    description: Development Server
  - url: https://staging.api.example.com/v1
    description: Staging Server
1
2
3
4
5
6
7

悪い例:

servers:
  - url: https://prod.api.example.com/v1
    description: Production Server
1
2
3

# paths

API の利用可能なエンドポイントと操作方法を記載する。

  • API ごとに機能 ID を定義している場合、paths 配下の各パスは機能 ID の昇順に定義する

    良い例:

    paths:
      /users:
        get:
          summary: API-101 ユーザ一覧取得
      /products:
        get:
          summary: API-201 商品一覧取得
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    paths:
      /products:
        get:
          summary: API-201 商品一覧取得
      /users:
        get:
          summary: API-101 ユーザ一覧取得
    
    1
    2
    3
    4
    5
    6
    7
  • URL パスが複数の単語からなる場合、ケバブケースで表現する

    良い例:

    paths:
      /product-owners:
        get:
          ...
    
    1
    2
    3
    4

    悪い例:

    paths:
      /productOwners:
        get:
          ...
    
    1
    2
    3
    4
  • HTTP メソッドは GET, POST, PUT, PATCH, DELETE の順に定義する

    良い例:

    paths:
      /products:
        get:
          ...
        post:
          ...
    
    1
    2
    3
    4
    5
    6

    悪い例:

    paths:
      /products:
        post:
          ...
        get:
          ...
    
    1
    2
    3
    4
    5
    6
  • 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 の論理的なグループを指定する。

  • タグオブジェクトとして事前定義したタグの中から選択する

    良い例:

    paths:
      /users:
        get:
          tags:
            - users
          ...
    tags:
      - name: users
    
    1
    2
    3
    4
    5
    6
    7
    8

    悪い例:

    paths:
      /users:
        get:
          tags:
            # タグオブジェクトとして定義されていないタグが指定されている
            - users
          ...
    tags: []
    
    1
    2
    3
    4
    5
    6
    7
    8
  • 1 API につき 1つのタグを指定する

    良い例:

    paths:
      /users:
        get:
          tags:
            - users
          ...
    
    1
    2
    3
    4
    5
    6

    悪い例:

    paths:
      /users:
        get:
          # 複数のタグが指定されている
          tags:
            - users
            - admin
          ...
    
    1
    2
    3
    4
    5
    6
    7
    8

# paths > {path} > {method} > summary

API の操作概要を記載する。

  • API ごとに機能 ID や機能名があるのであれば記載する

    良い例:

    paths:
      /users:
        get:
          summary: API-001 ユーザ一覧取得 
    
    1
    2
    3
    4

# paths > {path} > {method} > description

API の振る舞いの詳細や注意点を記載する。
別途参照させるべき設計書があるのであれば、設計書へのリンクを記載しても良い。

良い例:

paths:
  /users:
    get:
      description: [API詳細設計書(API-001)](https://example.com/API-001.md)
1
2
3
4

# paths > {path} > {method} > operationId

API を識別するための一意な文字列を記載する。

  • HTTP メソッドと URL パスの組み合わせをキャメルケースで表現する

    良い例:

    paths:
      /users:
        get:
          operationId: getUsers
          ...
      /products/{product_id}:
        put:
          operationId: putProductsProductId
          ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    悪い例:

    paths:
      /users:
        get:
          operationId: get_users
          ...
    
    1
    2
    3
    4
    5
  • OpenAPI ドキュメントエディタとして広く使用される Stoplight が提供するLinter (opens new window)の定義としては、ケバブケースが標準になっているため、Stoplight を使用する場合はケバブケースで表現しても良い

# paths > {path} > {method} > parameters

API のリクエストパラメータを記載する。

  • クエリパラメータはスネークケースで表現する

    良い例:

    paths:
      /users:
        get:
          ...
          parameters:
            - name: account_type
              in: query
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    paths:
      /users:
        get:
          ...
          parameters:
            - name: account-type
              in: query
    
    1
    2
    3
    4
    5
    6
    7
  • クエリパラメータは HTTP メソッドが GET, DELETE の場合にのみ指定する

    良い例:

    paths:
      /users:
        get:
          ...
          parameters:
            - name: account_type
              in: query
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    paths:
      /users:
        post:
          ...
          parameters:
            - name: acaccount_type
              in: query
    
    1
    2
    3
    4
    5
    6
    7
  • ヘッダはハイフンを区切り文字とするパスカルケースで表現する

    良い例:

    paths:
      /users:
        post:
          ...
          parameters:
            - name: Content-Type
              in: header
    
    1
    2
    3
    4
    5
    6
    7

    悪い例:

    paths:
      /users:
        post:
          ...
          parameters:
            - name: ContentType
              in: header
    
    1
    2
    3
    4
    5
    6
    7

# paths > {path} > {method} > requestBody

API のリクエストボディを記載する。

  • 標準仕様の describing-request-body (opens new window) の章にも記載がある通り、リクエストボディは HTTP メソッドが POST, PUT, PATCH の場合のみ指定する

    良い例:

    paths:
      /users:
        post:
          ...
          requestBody:
            required: true
            content:
              application/json:
                ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    悪い例:

    paths:
      /users:
        get:
          ...
          # HTTP メソッドが GET の場合にリクエストボディを指定
          requestBody:
            ...
    
    1
    2
    3
    4
    5
    6
    7
  • リクエストボディそのものは通常複数の API を跨いで再利用されるものではないため、原則 components オブジェクトとして共通化(コンポーネント化)を行わない

    • openapi-generator (opens new window)を使用する場合は、コンポーネント化をせず、title を指定することで名称の指定が可能となる
    • oapi-codegen (opens new window)を使用する場合は、名称を指定するためにコンポーネント化が必要となるが、極力コンポーネント化せずデフォルトの名称を使用することを推奨する

    良い例:

    paths:
      /users:
        post:
          ...
          requestBody:
            required: true
            content:
              application/json:
                ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    悪い例:

    paths:
      /users:
        get:
          ...
          requestBody:
            # コンポーネント化したリクエストボディを参照
            $ref: '#/components/requestBodies/ReqPostProductsBody'
    
    components:
      requestBodies:
        ReqPostProductsBody:
          content:
            application/json:
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

# paths > {path} > {method} > responses

API のレスポンスを記載する。

  • 正常系(2xx)のレスポンスは通常複数の API を跨いで再利用されるものではないため、原則 components オブジェクトとして共通化(コンポーネント化)を行わない

    • openapi-generator (opens new window)を使用する場合は、コンポーネント化をせず、title を指定することで名称の指定が可能となる
    • oapi-codegen (opens new window)を使用する場合は、レスポンスの構造体を出力するために strict-server オプションを true に指定する必要がある。名称を指定するためにコンポーネント化が必要となるが、極力コンポーネント化せずデフォルトの名称を使用することを推奨する

    良い例:

    paths:
      /products:
        post:
          responses:
            '200':
              description: 200 OK
              content:
                application/json:
                  ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    悪い例:

    paths:
      /products:
        post:
          responses:
            '200':
              # コンポーネント化したレスポンスオブジェクトを参照
              $ref: '#/components/responses/BadRequest'
    
    components:
      responses:
        BadRequest:
          description: 200 OK
          content:
            application/json:
              ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • 異常系(4xx, 5xx)のレスポンスは個別に定義するのではなく、事前に components オブジェクトとして定義を行い $ref で参照する

    良い例:

    paths:
      /products:
        post:
          responses:
            '400':
              # コンポーネント化したレスポンスオブジェクトを参照
              $ref: '#/components/responses/BadRequest'
    
    components:
      responses:
        BadRequest:
          description: 400 Bad Request
          content:
            application/json:
              ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    悪い例:

    paths:
      /products:
        post:
          responses:
            '400':
              # レスポンスオブジェクトを個別に定義
              description: 400 Bad Request
              content:
                application/json:
                  ...
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

# paths > {path} > {method} > security

API の認証方式を記載する。

  • 通常はルートレベルの security で API 共通的な認証方式を設定し、個々の API で個別に設定は行わない

  • ヘルスチェックのような認証を通す必要がない API のみ、上書きで定義する

    良い例:

    paths:
      /session:
        post:
          ...
          # 認証しない場合のみ個別で定義
          security: []
    
    1
    2
    3
    4
    5
    6

# components

API 定義で利用する共通のデータモデルを定義する。定義方針は下記の通りである。

フィールド名 方針
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

API 共通的なリソースやエラー等のドメインオブジェクトを記載する。

  • 名称はアッパーキャメルケースで定義する
  • 名称は単数形で定義する
  • type に複数の型を定義しない
  • typenull は原則指定しない(null 値を用いる代わりに、キー自体を含めない)
    差分更新APIの場合にあるとおり、空更新を行う場合は空文字を利用する
  • allOf, anyOf, oneOf は利用しない

良い例:

components:
  schemas:
    # 共通で使用するリソースを表すオブジェクト
    Product:
      type: object
      properties:
        ...
    User:
      type: object
      properties:
    # 共通で使用するエラーを表すオブジェクト
    ProblemDetailError:
      type: object
      properties:
        ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# components > responses

API 共通的なレスポンスを記載する。主に異常系(4xx, 5xx)のレスポンスを定義する。

  • 名称はアッパーキャメルケースで定義する
  • 異常系(4xx, 5xx)のレスポンスの場合、名称にステータスコードの名称(例. BadRequest, Unauthorized)を用いる

良い例:

components:
  schemas:
    ProblemDetailError:
      type: object
      properties:
        ...
  responses:
    # HTTP ステータスコード 400 のレスポンスオブジェクト
    BadRequest:
      description: 400 Bad Request
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ProblemDetailError"
    # HTTP ステータスコード 401 のレスポンスオブジェクト
    Unauthorized:
      description: 401 Unauthorized
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/ProblemDetailError"
    ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

正常系のレスポンスの例としてはファイルアップロード・ダウンロードのレスポンスなどが該当する。
個別のアプリケーション要件でブレが少なく、複数のエンドポイントで用いられる場合に定義する。オブジェクトのスキーマは、schemas に切り出して定義し、コード生成ツールのために型情報を付与させる。

良い例:

components:
  schemas:
    SignedURL:
      type: object
      properties:
        signed_url:
          type: string
          format: uri
        expired_at:
          type: string
          format: date-time
  responses:
    BlobUpload:
      description: BLOB(Binary Large Object) upload using presigned url
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/SignedURL"
    BlobDownload:
      description: BLOB(Binary Large Object) download using presigned url
      content:
        application/json:
          schema:
            "$ref": "#/components/schemas/SignedURL"
    ImageBinary:
      description: An image
      content:
        image/*:
          schema:
            type: string
            format: binary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# components > parameters

API 共通的なリクエストパラメータ(パスパラメータ、クエリパラメータ、ヘッダ, Cookie)を記載する。

# パスパラメータ
  • API 全体で利用されるパスパラメータが必要なケースが想定されないため、原則定義しない
    特定リソースの操作(例えば更新と削除)を行う際のリソース ID はパスパラメータとして再利用できるが、コンフリクトを避けるため原則共通化は行わない
# クエリパラメータ
  • API 全体で利用可能な共通のクエリパラメータ(例: 検索数の limit, offset)を定義する
  • 命名はクエリパラメータ名に Query というプレフィックスを付与する形式を推奨する

良い例:

paths:
  get:
    /products:
      parameters:
        - $ref: '#/components/parameters/QueryLimit'

components:
  parameters:
    QueryLimit:
      name: limit
      in: query
      required: false
      schema:
        type: integer
      description: 検索数上限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ヘッダ
  • API 全体で利用可能な共通のリクエストヘッダを定義する
  • 命名はヘッダ名に Header というプレフィックスを付与する形式を推奨する

良い例:

paths:
  post:
    /products:
      parameters:
        - $ref: '#/components/parameters/HeaderContentType'

components:
  parameters:
    HeaderContentType:
      name: Content-Type
      in: header
      schema:
        type: string
      required: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • API 全体で利用可能な共通の Cookie(例: CSRF 用のトークン)を定義する
  • 命名は Cookie 名に Cookie というプレフィックスを付与する形式を推奨する
  • Cookie 認証を定義する場合は、APIKey を利用する

良い例:

paths:
  get:
    /products:
      parameters:
        - $ref: '#/components/parameters/CookieCSRFToken'

components:
  parameters:
    CookieCSRFToken:
      name: csrftoken
      in: cookie
      required: true
      schema:
        type: string
      description: CSRFトークン
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# components > requestBodies

原則何も定義せず、リクエストボディは API 個別に記載する。

# components > headers

API 共通的なレスポンスヘッダを記載する。

  • 命名はヘッダ名からハイフンを除去した形式を推奨する

良い例:

paths:
  get:
    /products:
      responses:
        '200':
          headers:
            XCacheInfo:
              $ref: '#/components/headers/XCacheInfo'

components:
  headers:
    XCacheInfo:
      description: not cacheable; meta data too large
      schema:
        type: string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# components > securitySchemes

標準で用いる API 認証の定義を行う。

良い例:

components:
  securitySchemes:
    # Bearer トークンによる認証
    Bearer:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'Bearer トークン認証'
1
2
3
4
5
6
7
8

links (opens new window) は OpenAPI 3.0 の新機能の1つで、ある API レスポンスの値を用いて、別の API を呼び出す方法を明示できるセクションである。

興味深い機能であり、API のセマンティクスを伝えるのに有用であるが、本規約では記載しないことを推奨とする。

理由は下記の通りである。

  • 業務システムでは、業務フローを抑えておけば、API 操作フローの理解はそこまで難しくないことが多い
    • 逆に、API 同士の関係だけを示すだけでは業務モデリング図とのダブルメンテナンスになったり、中途半端になりうる
  • OAS 3.0 Support Backlog (opens new window) にあるように、2023/12/15時点では Swagger-UI が対応していない
    • links を書いたと言って、API ドキュメントに影響しない

# components > callbacks

callbacks (opens new window) は OpenAPI 3.0 の新機能の1つで、API サーバ側が指定されたコールバック URL を呼び出すという仕組みである。

仕様書には、EC ショップで購入のたびにマネージャーに通知を送るといった、何かしらの処理をトリガーにコールバック URL を呼び出す例が示されている。

利便性は高い仕様だが、本規約では記載しないことを推奨とする。

理由は下記の通りである。

  • コールバック URL 呼び出しの、エラーハンドリングが難しい
  • 業務システムでは欠損が許されない、または将来的に許されなくなる可能性があり、その場合にこの機能に頼ると想定以上の追加作業が発生する

コールバックのような仕組みを実現するには、別途キューイングのメッセージサービスの利用などを検討する。

# security

全 API に共通で適用されるセキュリティ設定を定義する。
業務システムの Web API において認証が全く存在しないケースは考えにくいため、本規約ではルートレベルで認証を設定し、個々の API への適応漏れを無くす。

良い例:

security:
  - Bearer: []
1
2

# tags

API を論理的にグループ化するためのタグを定義する。

  • ドキュメントやツールにとって重要であるため 必須 で指定する
  • name, description を必須項目とする
  • 単数形 で、小文字かつ半角スペース区切りで記載する
    半角スペース区切りで記載する理由は HTML ドキュメントで参照する場合の可読性を上げるためである
  • コード生成で利用される(Go においてはパッケージ、 TypeScript においてはクラスに相当する)ため、シンプルな命名にする

良い例:

tags:
  - name: product
    description: 製品
  - name: user account
    description: ユーザーアカウント
1
2
3
4
5

悪い例:

tags:
  - name: products
    description: 製品
  - name: user_account
    description: ユーザーアカウント
1
2
3
4
5

# externalDocs

参照情報としての URL を記載できる。
ただし、description にて参考情報となる URL を記載する方が、複数リンクを指定可能であるなど自由度が高く使いやすいため externalDocs は利用せず description の利用を推奨する。

良い例:

info:
  description: |-
    Some useful links:
    - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
    - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)

# 特別な場合を除き非推奨
externalDocs:
  description: Find out more about Swagger
  url: http://swagger.io
1
2
3
4
5
6
7
8
9
10

# 設計上のポイント

OpenAPI ドキュメントを作成する上での設計上ポイントをいくつか記載する。

# ファイルアップロード

詳細をみる

Web API におけるファイルアップロードのよく利用される実装手段は、大きく分けて以下の 3 手法に分類できる。

  1. ファイルのコンテンツを Base64 などにエンコードして、JSON の項目として設定し、リクエストボディで送る
    • メリット: 通常の JSON を扱うのとほぼ変わらないため楽。サムネイルなど限定されたユースケースの場合に向く
    • デメリット: 巨大なファイルを扱う場合などサーバリソース負荷が懸念。Base64 に変換する分 CPU 負荷は余計にかかる。ペイロードが膨れるためモバイルなどのクライアントでは帯域利用での懸念がある
  2. multipart/form-data ファイルを送信する
    • メリット: ファイルを Base64 に変換するといった作業が不要
    • デメリット: ブラウザ以外のクライアントにとって手間がかかる
  3. アップロード用に用いる、オブジェクトストレージの Signed URL を発行し、クライアントから直接ファイルをアップロードしてもらう

本規約でファイルアップロードについて上記の 3. Signed URL を推奨する。API 呼び出しとしては次のようなフローとする。

フローの ①、② はアプリケーション固有の紐づけルールにおいて Web API を設計すれば良いため、本規約で YAML の設定例は記載しない。フロー ② については Signed URL を用いたアップロードであり、アプリケーションの Web API 定義を書く必要はない。もし、監査ログなどのガバナンス上、直接オブジェクトストレージへの書き込みを許容されないケースは、B で Signed URL に相当する書き込み先を提供し、B を経由してファイルをアップロードする。

上記どちらのケースも OpenAPI 定義としてはシンプルであるため、記述例は割愛する。

# ファイルダウンロード

詳細をみる

ファイルアップロードと同様、オブジェクトストレージの Signed URL 経由を経由してのダウンロードさせる手法を推奨する。Web API としてはオブジェクトストレージにダウンロード用のファイルを書き込み、クライアントが取得するための Signed URL をレスポンスの JSON 項目に渡す方式である。

もし、サムネイルやアイコン画像など、ファイル容量がごく小さい場合は Base64 にエンコードして JSON に埋め込んで渡しても良い。線引をどこに設置するかは本規約で定義しない。

どちらのケースも OpenAPI 定義としてはシンプルであるため、記述例は割愛する。

# CORS

詳細をみる

CORS(Cross-Origin Resource Sharing)のために、options メソッドの追記は 原則不要 とする。

理由は以下である。

  • サーバ側
    • options メソッド対応は、API 仕様ではなく実装レベルの機能横断的な処理(Java における Servlet Filter や Spring の Interceptor、Go における Middleware など)で行うことが大半であり、コード生成が不要
  • クライアント側
    • options メソッドを用いるのはクライアントがブラウザであり、クライアントのアプリケーションコードが明示的にアクセスしないため、コード生成が不要
  • 使用面として
    • Access-Control-Allow-Origin がどのような値を返すか、呼び出し元によって動的な値を返したい場合があり、記載が困難なケースがある

ただし、Amazon API Gateway のようなサービスを利用する場合は、options メソッドの記載が必須である場合は除く^1 (opens new window)

# OpenTelemetry Traceparent HTTP Header

詳細をみる

OpenOpenTelemetry で用いるられるtraceparent (opens new window) のリクエストヘッダは OpenAPI で 原則不要 とする。

理由は以下である。

  • OpenTelemetry が定めるヘッダ類は、API 横断的に設定されるべきものであり、ミドルウェアやフレームワーク側などでの一律の制御を推奨するため
  • 記載することにより、OpenOpenTelemetry に対応していることを明記し開発者に周知できるメリットより、各アプリ開発者が生成されたコードで悩んだり、誤解されることを回避したいため

# バリデーション

詳細をみる

パラメータのバリデーションをどこまで厳密に定義すべきかという議論はしばしば行われる。

リクエストパラメータの各項目に対して、必須・型・桁・区分値・日付・正規表現のチェックが行える。レスポンスで用いるモデルについても同様に設定でき、enum, pattern 以外は API の利用者(クライアント)側の DB 設計などに必要な型桁情報を渡すのに有用であるため、できる限り詳しく指定する。

# 必須

必須パラメータのみ required: true を定義する

# デフォルト値

パラメータにデフォルト値がある場合はdefault を定義する。

# ex. enum
name: limit
type: number
format: integer
minimum: 1
maximum: 100
default: 20
description: 検索結果の項目数上限(1~100が指定可能)
1
2
3
4
5
6
7
8

【注意】API 公開後に、default 値を変更してはならない(API の互換性が崩れるため)。もし変更する場合は、API のバージョンを上げること。

# 型・フォーマット

型(type)は string(文字列), number(数値), integer(整数値), boolean(真偽値) array(配列) のうちどれか指定する。

フォーマット(format)は以下の型の詳細情報を示すもので、可能な限り設定する。

  • integer(整数)
    • int32, int64
  • number(数値)
    • float, double
  • string(バイナリ)
    • byte: Base64 でエンコードされた文字列
    • binary: バイト配列
  • string(日付)
    • date: RFC3339 (opens new window) full-date(例: 2023-07-21)
      • 項目名は _on を接尾辞につけることを推奨とする
    • date-time: RFC3339 (opens new window) date-time(例: 2023-07-21T17:32:28Z)
      • 項目名は _at を接尾辞につけることを推奨とする
  • string(その他)
    • password: Swagger UI で入力が隠される
    • その他、 email, uuid など Open API 仕様に存在しない任意のフォーマットを独自のドキュメント生成などのために記載しても良い

OpenAPI 3.0では 2.0 に存在した file type は存在しない。もし同等の指定をしたい場合は、以下の様に指定する。

type: string
format: binary  # binary file contents
1
2

#

データ型によって、利用できる桁を指定する項目が異なる。可能な限り設定する。

  • 文字列
    • 最大桁数:maxLength
    • 最小桁数:minLength
  • 数値または整数値
    • 最小値(境界値を含む):minimum
    • 最大値(境界値を含む):maximum
    • 境界値を含まない場合のみexclusiveMinimum: trueまたはexclusiveMaximum: trueを定義する。minimum, maximum で代用できる場合は利用しない
  • 配列:
    • 最大要素数:maxItems
    • 最小要素数:minItems
    • required: trueの場合は原則としてminItems: 1を定義する
    • uniqueItems は必須で指定する(通常は一意であるべき)

【注意】API 公開後に、レスポンスの maxLength を以前より大きい値に変更してはならない。レスポンスの maxLength など API 利用者側システムの DB の ERD 定義のインプットになる事が多いため。もし行う場合は API のバージョンを上げることや、連携先に桁数変更の旨を調整するなどの考慮を行う。

# 区分値

区分値の場合は enum 属性を利用し、descriptionには区分値の論理名を記載する。

name: gender
type: string
enum: ["0", "1", "2", "9"]
description: |
  性別
    0: 不明
    1: 男
    2: 女
    9: 適用不能
1
2
3
4
5
6
7
8
9

OpenAPI 3.0 では区分値の再利用ができるため、横断的に用いる区分値は components 側で定義する。

paths:
  /products:
    get:
      parameters:
      - in: query
        name: gender
        required: true
        schema:
          $ref: '#/components/schemas/Gender'
components:
  schemas:
    Gender:
      type: string
      enum: ["0", "1", "2", "9"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 固定値

固定値 の場合も enum を 1 つだけ指定して表現する。この場合もレスポンスで利用する場合は指定しない

name: file_layout
type: string
enum: ["json"]
description: ファイルレイアウト
1
2
3
4

# その他(正規表現)

正規表現で表現できる文字列はpatternを利用して定義する。桁や区分値で代替できる場合は、pattern を用いない

例:

remind_time:
  type: string
  description: リマインド時刻。(hh:mm)形式
  example: 23:59
  pattern: "^(2[0-3]|[01][0-9]):([0-5][0-9])$"
1
2
3
4
5

# 値が存在しないという状態の表現

詳細をみる

原則 null を用いず、パラメータのキー自体を含めないこと(undefined)による表現を行う。
詳細は技術ブログ記事 (opens new window)を参照されたい

# ファイル分割

詳細をみる

OpenAPI ドキュメントは単一のファイルで構成することも複数の分割されたファイルで構成することもできるが、複数のファイルに分割することを推奨する。
理由は下記の通りである。

  • API ごとに担当者を分けて設計する場合などに、複数人による編集によって意図しないコンフリクトが発生することを防ぐ
  • ファイルの肥大化による、可読性の低下を防ぐ

# 分割方法の選定

開発方針や OpenAPI の使用用途に合わせて、都合の良いファイルの分割方法を採用する。例えば、以下のような方法がある。

  1. API ごとに設計担当者を分けて、それぞれに OpenAPI を編集する場合は、API の単位で分割する
  2. テストツールとして stoplightio/prism (opens new window)を使用する場合、テストケースごとにデータファイルを作成して、examples にファイルパスを指定する

# サンプル説明

分割方法1, 2の両方に当てはまる場合のサンプルを用いて説明する。openapi.yaml とディレクトリ構成は下の通り。サンプルの全量は サンプルzip Downloadからダウンロード可能。

  • 機能単位(path, method 単位)にディレクトリを作成して、それぞれの定義ファイルを格納する。ディレクトリ名は {path}_{method} とすると管理し易い

  • componentsschemas には、

    • API 間で同じモデルを使用する場合は共通化して記載する(例えば、Pet
    • 各 API のリクエスト/リスポンスモデルの中で、モデルがネストする場合は、各モデルの単位で書き出す(例えば、PetDetail, Pedigree
    • ※schemas のモデルの中身は別ファイルに定義が可能だが、大本の openapi.yaml にも命名のみ定義が必要。openapi.yaml の定義が無いと swaggerUI で確認した際に schemas 定義が見えなくなってしまう
    ファイル分割例: openapi.yaml
      openapi: "3.0.3"
      info:
        version: 1.0.0
        title: Swagger Petstore
        license:
          name: MIT
      servers:
        - url: http://petstore.swagger.io/v1
      tags:
        - name: pets
          description: Everything about your Pets
      paths:
        /pets:
          get:
            $ref: "./pets_get/pets_get.yaml#/operation"
          post:
            $ref: "./pets_post/pets_post.yaml#/operation"
        /pets/{petId}:
          get:
            $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/operation"
      components:
        schemas:
          PetDetail:
            $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/components/schemas/PetDetail"
          Pedigree:
            $ref: "./pets-pet-id_get/pets-pet-id_get.yaml#/components/schemas/Pedigree"
          Pet:
            $ref: "./common/pet.yaml"
          Error:
            $ref: "./common/error.yaml"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    ファイル分割例: ディレクトリ構成
      ├─openapi.gen.yaml
      ├─openapi.yaml
      │
      ├─common
      │  ├─error.yaml
      │  └─pet.yaml
      │
      ├─pets-pet-id_get
      │  ├─pets-pet-id_get.yaml
      │  └─examples
      │         └─res_example1.yaml
      │
      ├─pets_get
      │  ├─pets_get.yaml
      │  └─examples
      │         ├─res_example1.yaml
      │         └─res_example2.yaml
      │
      └─pets_post
          ├─pets_post.yaml
          └─examples
                └─req_example1.yaml
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  • openapi.yamlpaths に記載した API ファイルは以下のように作成する

  • 複数 API 間に共通のモデルについては openapi.yaml に指定したキー(../openapi.yaml#/components/schemas/Pet)を参照する

  • examples には、各 API のテストケース ID をキーとして指定(ResExample1)し、value に該当するテストケースのデータファイルパスを指定(./examples/res_example1.yaml)する。ファイル名は、指定したキーをスネークケースに変換したものを使用するとよい

    API別ファイルの記載例: pets-pet-id_get.yaml
      operation:
        operationId: get-pets-pet-id
        summary: Details for a pet
        tags:
          - pets
        parameters:
          - name: petId
            in: path
            required: true
            description: The id of the pet to retrieve
            schema:
              type: string
        responses:
          "200":
            description: Expected response to a valid request
            content:
              application/json:
                schema:
                  $ref: "#/components/responses/ResPetsPetIdGet"
                examples:
                  ResExample1:
                    value:
                      $ref: "./examples/res_example1.yaml"
          "404":
            description: not found error
            content:
              application/json:
                schema:
                  $ref: "../openapi.yaml#/components/schemas/Error"
          "500":
            description: unexpected error
            content:
              application/json:
                schema:
                  $ref: "../openapi.yaml#/components/schemas/Error"
      components:
        schemas:
          PetDetail:
            type: object
            properties:
              breeder:
                type: string
              date_of_birth:
                type: string
                format: date
              pedigree:
                $ref: "#/components/schemas/Pedigree"
          Pedigree:
            required:
            - registration_no
            - date_of_registration
            - pedigree_image
            type: object
            properties:
              registration_no:
                type: integer
                format: int64
              date_of_registration:
                type: string
                format: date
              pedigree_image:
                type: string
        responses:
          ResPetsPetIdGet:
            required:
            - pet
            - pet_detail
            type: object
            properties:
              pet:
                $ref: "../common/pet.yaml"
              pet_detail:
                $ref: "#/components/schemas/PetDetail"
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
  • OpenAPI の使用用途により、分割ファイルを1つのファイルにまとめる必要がある場合には、例えばswagger-cli (opens new window)を使用して以下コマンドを実行する

    swagger-cli bundle openapi.yaml --outfile openapi.gen.yaml --type yaml
    
    1
    ファイルBundle後: openapi.gen.yaml
    openapi: 3.0.3
    info:
      version: 1.0.0
      title: Swagger Petstore
      license:
        name: MIT
    servers:
      - url: 'http://petstore.swagger.io/v1'
    tags:
      - name: pets
        description: Everything about your Pets
    paths:
      /pets:
        get:
          summary: List all pets
          operationId: get-pets
          tags:
            - pets
          parameters:
            - name: limit
              in: query
              description: How many items to return at one time (max 100)
              required: false
              schema:
                type: integer
                maximum: 100
                format: int32
          responses:
            '200':
              description: A paged array of pets
              headers:
                x-next:
                  description: A link to the next page of responses
                  schema:
                    type: string
              content:
                application/json:
                  schema:
                    type: array
                    maxItems: 100
                    items:
                      type: object
                      required:
                        - id
                        - name
                        - category
                        - age
                        - sex
                      properties:
                        id:
                          type: integer
                          format: int64
                        name:
                          type: string
                          maxLength: 50
                        category:
                          type: string
                          maxLength: 10
                        sub_category:
                          type: string
                          maxLength: 50
                        age:
                          type: integer
                          format: int32
                        sex:
                          type: string
                          maxLength: 6
                        note:
                          type: string
                          maxLength: 200
                        tag:
                          type: string
                          maxLength: 20
                  examples:
                    ResExample1:
                      value:
                        - id: 10001
                          name: ToyPoodle
                          category: dog
                          sub_category: ToyPoodle
                          age: 1
                          sex: male
                          note: friendly
                          tag: dog10001
                        - id: 10002
                          name: Chihuahua
                          category: dog
                          sub_category: Chihuahua
                          age: 1
                          sex: female
                          note: friendly
                          tag: dog10002
                        - id: 10003
                          name: Shiba
                          category: dog
                          sub_category: Shiba
                          age: 1
                          sex: male
                          note: friendly
                          tag: dog10003
                        - id: 10004
                          name: MiniatureDachshund
                          category: dog
                          sub_category: MiniatureDachshund
                          age: 1
                          sex: female
                          note: friendly
                          tag: dog10004
                    ResExample2:
                      value: []
            '404':
              description: not found error
              content:
                application/json:
                  schema:
                    type: object
                    required:
                      - code
                      - message
                    properties:
                      code:
                        type: integer
                        format: int32
                      message:
                        type: string
            '500':
              description: unexpected error
              content:
                application/json:
                  schema:
                    $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema'
        post:
          summary: Register a pet
          operationId: post-pets
          tags:
            - pets
          requestBody:
            content:
              application/json:
                schema:
                  required:
                    - pet
                  type: object
                  properties:
                    pet:
                      $ref: '#/paths/~1pets/get/responses/200/content/application~1json/schema/items'
                examples:
                  ReqExample1:
                    value:
                      pet:
                        id: 10005
                        name: FrenchBulldog
                        category: dog
                        sub_category: FrenchBulldog
                        age: 1
                        sex: male
                        note: friendly
                        tag: dog10005
            required: false
          responses:
            '201':
              description: Null response
            '404':
              description: not found error
              content:
                application/json:
                  schema:
                    $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema'
            '500':
              description: unexpected error
              content:
                application/json:
                  schema:
                    $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema'
      '/pets/{petId}':
        get:
          summary: Details for a pet
          operationId: get-pets-pet-id
          tags:
            - pets
          parameters:
            - name: petId
              in: path
              required: true
              description: The id of the pet to retrieve
              schema:
                type: string
          responses:
            '200':
              description: Expected response to a valid request
              content:
                application/json:
                  schema:
                    required:
                      - pet
                      - pet_detail
                    type: object
                    properties:
                      pet:
                        $ref: '#/paths/~1pets/get/responses/200/content/application~1json/schema/items'
                      pet_detail:
                        type: object
                        properties:
                          breeder:
                            type: string
                          date_of_birth:
                            type: string
                            format: date
                          pedigree:
                            required:
                              - registration_no
                              - date_of_registration
                              - pedigree_image
                            type: object
                            properties:
                              registration_no:
                                type: integer
                                format: int64
                              date_of_registration:
                                type: string
                                format: date
                              pedigree_image:
                                type: string
                  examples:
                    ResExample1:
                      value:
                        pet:
                          id: 10001
                          name: ToyPoodle
                          category: dog
                          sub_category: ToyPoodle
                          age: 1
                          sex: male
                          note: friendly
                          tag: dog10001
                        pet_detail:
                          breeder: BreederName
                          date_of_birth: '2023-10-31'
                          pedigree:
                            registration_no: 11111111
                            date_of_registration: '2023-10-31'
                            pedigree_image: 9j2wBDAA...8QAPxAAAQQABAMGBAYDAAEDAg
            '404':
              description: not found error
              content:
                application/json:
                  schema:
                    $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema'
            '500':
              description: unexpected error
              content:
                application/json:
                  schema:
                    $ref: '#/paths/~1pets/get/responses/404/content/application~1json/schema'
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255

# License

CC-By-4.0 (opens new window)