🎯

OpenAPI Specification ざっくり説明

2023/01/03に公開約19,200字

はじめに

次のリンクにある OpenAPI Specification (OAS) の内容を「ざっくり説明」として書き出しました。記述例など自分自身が後で理解しやすいように適当に変えています。

OpenAPIのもっと詳細な仕様はこちらのOpenAPI Specification v3.1.0を参照ください。

OpenAPIドキュメントの構造

ドキュメントの文法

YAMLかJSONで書く。

JSON:

{
  "anObject": {
    "aNumber": 42,
    "aString": "This is a string",
    "aBoolean": true,
    "nothing": null,
    "arrayOfNumbers": [
      1,
      2,
      3
    ]
  }
}

YAML:

# コメントが書ける。
anObject:
  aNumber: 42
  aString: This is a string
  aBoolean: true
  nothing: null
  arrayOfNumbers:
    - 1
    - 2
    - 3

複数のファイルに分割も可能。

必要最小限のドキュメント

ドキュメントのルートオブジェクトは OpenAPI Objectopenapiinfoの2つのフィールドが必須。あと、pathscomponentswebhooksのどれか一つが必要。

  • openapi(string): OASのバージョン。
  • info(Info Object): APIについての全般的な情報。
    • title(string):
    • version(strng): APIドキュメントのバージョン。
  • paths(Paths Object): APIのパラメータやレスポンスを含むエンドポイントについての定義。

以下は最小限のドキュメント定義例。

openapi: 3.1.0
info:
  title: 最小限のOpenAPIドキュメント
  version: 0.0.1
paths: {} # エンドポイント定義なし

APIエンドポイント

エンドポイント一覧

OASでは、APIエンドポイント(オペレーションやルートとも呼ばれる)は Paths と呼ばれる。ルートのOpenAPI Object 直下にあるpaths(Paths Object)はAPIが提供する全オペレーションが含まれる。

pathsの全てのフィールドは一つのAPIエンドポイントを定義するpath(PathItemObject)となる。エンドポイントの名前は一意とする。

パスはスラッシュ / 始まりとする必要があり、サーバーURLと結合してエンドポイントのURLになる。

Path Item Object

Path Item Object は、パスに対するgetputpostといったHTTPオペレーションを Operation Object でそれぞれ定義する。
summarydescriptionで全てのHTTPオペレーションに共通する事項について記載できる。

paths:
  /board:
    get:
      ・・・
    put:
      ・・・

Operation Object

Operation Object では基本的に、HTTPオペレーションのリクエストパラメーターやペイロード、レスポンスを定義する。

paths:
  /board:
    get:
      summary: ボード情報の取得
      description: 現在のボード状態と勝者を取得する。
      parameters:
        ・・・
      responses:
        ・・・

Responses Object

Responses Object では、リクエストに対するサーバーのレスポンスを定義する。それぞれのフィールド名は引用符で囲まれたHTTPレスポンスコードで示し、レスポンスの内容は Response Object で定義する。

paths:
  /board:
    get:
      responses:
        "200":
          ・・・
        "404":
          ・・・

Response Object

Response Object は、このHTTPオペレーションにおけるレスポンスの説明を記載するdescriptionを必須のフィールドとして含む。このフィールドで一般的なHTTPレスポンスコードの意味に加えてAPI
固有の説明を記載できる。

最も重要なフィールドはcontentフィールドで、そのレスポンスで返却できる内容を定義する。

paths:
  /board:
    get:
      responses:
        "200":
          description: 処理が全て成功した場合のレスポンス。
          content:
            ・・・

メッセージ本文の内容

Response Objectcontentフィールドでレスポンスの内容を定義するが、リクエストのペイロードとしても使う。

contentフィールド

contentフィールドは、Response ObjectRequest Body Object で定義するフィールド。
これは、RFC6838のメディアタイプ(MIMEタイプ)と Media Type Object のマップになっている。

これは、メディアタイプによって異なる内容を返すように定義できる。メディアタイプにはワイルドカードの指定が可能。この場合、より具体的に指定されているメディアタイプの内容が優先される。

content:
  application/json:
    ・・・
  text/html:
    ・・・
  text/*:
    ・・・

Media Type Object

Media Type Object は、レスポンスやリクエストボディ内容の構造を定義する。また、モックとして利用できるように具体例も示すことができる。

content:
  application/json:
    schema:
      ・・・

Schema Object

Schema Objectは、プリミティブ型(integer、stringなど)、配列やオブジェクトなどtypeフィールドの指定に応じたデータ型を定義する。

typeフィールドにはnumberintegerstringbooleanarrayおよびobjectを指定できる。指定した型に応じ、更にデータ形式を指定するためのフィールドが利用できる。

integer型はminimummaximumで値の範囲を制限できる。

content:
  application/json:
    schema:
      type: integer
      minimum: 1
      maximum: 100

string型は文字列長をminLengthmaxLengthで制限できる。

また、型に関係なく指定できる値のセットをenum配列を用いて制限できる。

content:
  application/json:
    schema:
      type: string
      enum:
      - Alice
      - Bob
      - Carl

array型は、itemsフィールドが必須となっていてSchema Objectで配列の要素について型を定義する。更に、minItemsmaxItemsで配列サイズを制限できる。

content:
  application/json:
    schema:
      type: array
      minItems: 1
      maxItems: 10
      items:
        type: integer

object型は、propertiesフィールドにオブジェクトのプロパティを定義する。このフィールドは、プロパティ名とSchema Objectのマップ配列となる。

content:
  application/json:
    schema:
      type: object
      properties:
        productName:
          type: string
        productPrice:
          type: number

オペレーションのパラメータとメッセージペイロード

OpenAPIでは入力データとして パラメータリクエストボディ(メッセージペイロード) の2通りの方法を定義できる。通常、パラメータはリソースの識別に使い、メッセージペイロードはリソースの内容を提供する。

Parameter Object

Path Item ObjectOperation Objectparametersフィールドは、Parameter Object を要素とする配列となっている。Path Item Object の定義は、そのパスの全てのオペレーションで共有される。オペレーション単位でオーバーライドできるが、削除はできない。

Parameter Object では、次の必須フィールドを定義する。

  • in(string): パラメータが使われるロケーション。
  • name(string): パラメーターの名前。大文字と小文字を区別し、ロケーション単位で一意とする。

任意フィールドとして次のものを含む。

  • description(string): 使用例などを含むドキュメンテーション。
  • required(boolean): このパラメータが必須かどうかを示す。デフォルトはfalse

以降で示す追加フィールドで、パラメータの型、形式やシリアル化を指定できる。

パラメータ ロケーション

パラメータは、inフィールドで指定できるいろんなロケーションで使用できる。主なものを次に示す。

  • path: このパラメータがオペレーションのパス(URL)の一部であることを示す。パラメータの名前はテンプレート式として中カッコ {}でパスに定義されている必要がある。
paths:
  /users/{id}:
    get:
      parameters:
      - name: id
        in: path
        required: true

注意: パスでパラメータを使う場合、requiredフィールドをtrueで定義する必要がある。

  • query: パラメータは、オペレーションURLのクエリー文字列部分に追加される。
paths:
  /users:
    get:
      parameters:
      - name: id
        in: query

この場合のURLは、/users?id=1234のようになる。

  • header: パラメータはリクエストの一部として、カスタムHTTPヘッダーとして送信される。ヘッダーの名前は大文字と小文字を区別しない。

パラメータの型

ほとんどの場合、パラメータの型はschemaフィールドで Schema Object を使って指定される。Schema Object はプリミティブ型や複合型(配列やオブジェクトなど)を定義でき、更に追加の制限を定義できる。

parameters:
- name: id
  in: query
  schema:
    type: integer
    minimum: 1
    maximum: 100

もっと高度なシナリオで、contentフィールドを代わりに使える。これは、メディアタイプから Media Type Object への単一エントリーマップとして定義する。

注意: 正確には、schemacontentのいずれか一つを定義する必要があり、同時に定義することはできない。

パラメータのシリアル化制御

styleフィールドは、どのようにパラメータがシリアル化されるかを定義し、シリアル化の内容はパラメータの型に依存する。

styleフィールドの主なものsimpleformlabelおよびmatrixについて以降に示す。

  • プリミティブ型: 名前がidで値が 1234 の整数(integer)型パラメータの場合
style: simple form label matrix
1234 id=1234 .1234 ;id=1234
  • 配列型: 名前がidsで値が 1、2 および 3 の整数(integer)を含む配列型パラメータの場合
    explodeフィールドは、配列の要素を個別のパラメータとして分割するかを指定できる。
style: simple form label matrix
explode=falseを指定 1,2,3 ids=1,2,3 .1.2.3 ;ids=1,2,3
explode=trueを指定 1,2,3 ids=1&ids=2&ids=3 .1.2.3 ;ids=1;ids=2;ids=3
  • オブジェクト型: 名前がcolorで、値が 1、2 および 3 の整数(integer)フィールド R、G および B を含むオブジェクト型パラメータの場合
    explodeフィールドは、フィールドを個別のパラメータとして分割するかを指定できる。
style: simple form label matrix
explode=falseを指定 1,2,3 ids=1,2,3 .1.2.3 ;ids=1,2,3
explode=trueを指定 1,2,3 ids=1&ids=2&ids=3 .1.2.3 ;ids=1;ids=2;ids=3

シリアル化の詳細は、Parameter Object の仕様ページを参照。

Request Body Object

データベースのレコードを更新する際、一般的にはパラメータでレコードを識別し、メッセージ本文で更新内容となる新しいコンテンツを指定する。

リクエストのメッセージ本文は、Operation ObjectrequestBodyフィールドである Request Body Object で指定する。

paths:
  /board:
    put:
      requestBody:
        ・・・

Request Body Object の唯一の必須フィールドはcontentで、メッセージ本文の内容の説明したもの。

Request Body Object は、メッセージ本文の簡単な説明を記述するdescriptionフィールドと、メッセージ本文を必須とするかを示すrequiredフィールドを持つ。

定義の再利用

OpenAPI ドキュメントでは、定義を再利用して冗長性を取り除く仕組みがある。

Components Object

ルートであるOpenAPI Object直下のcomponentsフィールドであるComponents Objectは、ドキュメントの他の箇所から再利用できるオブジェクトの定義が含まれている。

OpenAPI ドキュメントに定義するほとんどのオブジェクトはコンポーネントへの参照に置き換えることができ、ドキュメントサイズとメンテナンスのコストを大幅に減らしてくれる。

参照できるオブジェクトはschemasresponsesparametersなど Components Object のフィールドとなっているものだけ。

Components Object の各フィールドは、再利用のためのオブジェクトにマッピングされるコンポーネント名となる。これらのオブジェクトの種類は、親フィールドに一致しないといけない。例えば、schemasフィールドのオブジェクトは Schema Objects でなければいけない。

次の例では二つのオブジェクトを定義している。

  • coordinateはスキーマコンポーネントで、Schema Object が期待される箇所で参照できる。
  • rowParamはパラメータコンポーネントで、Parameter Object が期待される箇所で参照できる。
components:
  schemas:
    coordinate:
      type: integer
      minimum: 1
      maximum: 3
  parameters:
    rowParam:
      name: row
      in: path
      required: true

Reference Object

Components Objectで対応している型のOpenAPIオブジェクトは、任意のコンポーネントを指す Reference Object で置き換えできる。

Reference Object は JSON Reference で表し、$refフィールドに参照オブジェクトを指すURIを指定しただけのオブジェクト。

$ref: 'https://gigantic-server.com/schemas/Monster/schema.yaml'

URIは絶対または相対で指定で、フラグメント識別子を含めることができる。

$ref: './another_file.yaml#rowParam'

次の例では、同じドキュメント内の定義を参照している。

パラメータコンポーネント(rowParamcolumnParamパラメータ)から同じスキーマコンポーネント(coordinateスキーマ)を参照している。また、/board/{row}/{column}パスから2つのパラメータを参照している。

components:
  schemas:
    coordinate:
      type: integer
      minimum: 1
      maximum: 3
  parameters:
    rowParam:
      name: row
      in: path
      required: true
      schema:
        $ref: "#/components/schemas/coordinate"
    columnParam:
      name: column
      in: path
      required: true
      schema:
        $ref: "#/components/schemas/coordinate"
paths:
  /board/{row}/{column}:
    parameters:
      - $ref: "#/components/parameters/rowParam"
      - $ref: "#/components/parameters/columnParam"

ドキュメントと具体例の記述

OpenAPIドキュメントはコンピュータで処理するための記述だけでなく、開発者が読むためのドキュメントも記述できる。

ドキュメンテーションフィールド

OpenAPI仕様のほとんどのオブジェクトは、開発者への追加情報を提供するためdescriptionフィールドを持っている。

例えば、パラメータの目的や仕様、他のパラメータとの相関などを記載できる。

paths:
  /audio/volume:
    put:
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: integer
              minimum: 0
              maximum: 10
              description:
                オーディオ主音量を指定する。
                0 は音量ゼロを指定する(ミュート状態)。 10 が最大音量。
                0 を指定した場合、アラーム音量には影響しない。
              ・・・

Path Ietm ObjectOperation Object および Example Object は説明が長くなりがちなため、要約を記述できるsummaryフィールドを持っている。

YAMLにおける長文説明の記述

JSONの場合、文字列は引用符で囲まれているので開始と終了が明確。YAMLの場合、複数行の文字列だと少し分かりづらい。

上記のYAML記載例のような長い説明では、インデントが小さい行が出てきた時点で終了と判断されるので、行の先頭にあるスペースが重要になる。この例は プレーンモード を使っていて特別な構文を使わないので記載が簡単だが、文字列にコロン:やハッシュ#といった文字を含む場合にYAMLパーサーを混乱させてしまうため、引用符で囲って回避してあげないといけない。

description: "Beware of lines containing colons and hashes like this: #"

改行位置を正確にコントロールしたい場合には更に2つのモードとして リテラルモード折りたたみモード が在り、説明の先頭行のインジケーター文字だけで有効になる。リテラルモード折りたたみモード では引用符は不要。

  • リテラルモード (パイプ|で示す。): YAMLファイルのソースにある改行は出力でそのまま維持される。

YAML source:

description: |
  This is a string
  in multiple lines.

  And an extra one.

Output:

This is a string
in multiple lines.

And an extra one.
  • 折りたたみモード (大なり記号>で示す。): 改行は削除され、出力は一つの文字列になる。但し、空行は改行になる。

YAML source:

description: >
  This is a string
  in multiple lines.

  And an extra one.

Output:

This is a string in multiple lines.
And an extra one.

CommonMark構文

descriptionフィールドでは、CommonMark 0.27 を使ってリッチなテキスト書式指定が可能。CommonMark構文の日本語訳

Example Object

一部のOpenAPIオブジェクトは、具体例をdescriptionフィールドに埋め込む代わりに明示的に表記でき、ツールで自動的に処理できるようになっている。

exampleフィールドでは一つの具体例を、examplesフィールドで複数の具体例を示すことができ、これによって次のことが可能になる。

  • ドキュメント中で具体例を特別にレンダリングする。
  • Example Objectモックサーバーの戻り値として利用する。

各オブジェクトで、exampleフィールドまたはexamplesフィールドのどちらかを記述できる。

exampleフィールド(Parameter ObjectMedia Type Object および Schema Object のフィールド)は、その親オブジェクトの形式に一致しないといけない。

schema:
  coordinate:
    type: integer
    minimum: 1
    maximum: 3
    example: 1

examplesフィールド(Parameter Object および Media Type Object のフィールド)は、具体例の名前と Example Object のマップになっている。Example Object には実際のコード(valueフィールド内またはexternalValueフィールドの外部参照)と、その説明のためのsumamryおよびdescriptionフィールドがある。

APIサーバー

定義したAPIにアクセス可能なサーバーを定義することができ、複数のURLや可変部分が含まれる。

Server Object

Server Object はAPIが提供されるベースURLを設定する。ルートの OpenAPI Object 、Path Item Object および Operation Objectservers配列フィールドとして定義できる。

servers配列フィールドの各要素は Server Object になっていて、サーバーのベースURLをurlフィールドに設定する。descriptionフィールドでサーバーの説明を記載することもできる。

servers:
- url: https://dev.server.com/v1
  description: 開発環境のサーバー
- url: https://test.server.com/v1
  description: テスト環境のサーバー
- url: https://www.server.com/v1
  description: 本番環境のサーバー

このベースURLに Paths Object で指定されている各APIエンドポイントをくっ付けることで完全なエンドポインURLになる。例えば、

servers:
- url: https://server.com/v1
paths:
  /users:
    get:

の場合、https://server.com/v1/usersのようにGETリクエストを実行してアクセスできることになる。

あるオペレーションに対して OpenAPI Object 、Path Item ObjectOperation Object など複数のservers配列フィールドで指定されている場合、いちばん低いレベルのものが適用される。例えば、

servers:
- url: https://server1.com
paths:
  /users:
    get:
      servers:
      - url: https://server2.com

の場合、/usersエンドポイントへのGETリクエストはhttps://server2.comで提供されることになる。

ドキュメントにサーバーが定義されていない場合、全てのAPIエンドポイントはOpenAPIドキュメントが提供されているロケーションからの相対パスと想定することになる。

サーバー変数

サーバーURLには、中カッコ {} で区切られた変数を含めることができる。

servers:
- url: https://{username}.server.com:{port}/{version}

これらの変数はvariablesフィールドで詳細を定義する必要がある。このフィールドは変数名と Server Variable Object のマップになっていて、サーバーURLに含まれている中カッコ内の変数名の一つに一致していないといけない。

Server Variable Object は次のフィールドを持っている。

  • default(string): 必須フィールドで、変数の値が指定されない場合に適用される。
  • enum(stringの配列): 変数の有効な値を配列で定義できる。このフィールドが定義されている場合、defaultフィールドの値は定義されている配列値に含まれている必要がある。
  • description(string): 変数についての説明を記載。

変数値は次のような方法で指定することができる。

  • OpenAPIジェネレーターでの生成時に指定
    • OpenAPIドキュメントからOpenAPIジェネレーターを使ってAPIクライアントやサーバースタブなどを生成する際のコマンドラインオプションとして指定できる。
    • 例: --server-variablesオプションによる指定
    openapi-generator-cli generate --server-variables username=test,port=443,version=v99
    
  • Swagger UIやIDEのプラグインなどツールによるAPI実行時に指定
    • ビジュアルツールでは変数値を入力指定できるように対応されている。
    • 例: Visual Sutdio Codeの拡張機能「OpenAPI (Swagger) Editor」の場合
      Visual Sutdio Codeの拡張機能「OpenAPI (Swagger) Editor」の場合

おわりに

書きながら参考にOpenAPI Specification v3.1.0を読んでいて

  • Security Scheme Object
  • Discriminator Object
  • XML Object

がちゃんとわからん...と感じましたので記事にして理解したいなと思っています。

Discussion

ログインするとコメントできます