🥒

OpenAPIのチュートリアルを自分用にわかりやすくカスタマイズした

2023/04/02に公開

業務で使っているけれど、ちゃんと理解してなかったので
一旦Tutorialをやってみました。

https://support.smartbear.com/swaggerhub/docs/tutorials/openapi-3-tutorial.html

概要掴むのに、とても良かったので、意訳しつつ理解したことをまとめました。
詳細はドキュメントを読みにいくのが良いと思います。

OpenAPIとは?

OpenAPI 3.0 is an open-source format for describing and documenting APIs.

OpenAPIとは、API仕様の記述フォーマットです。

2010年にReverb Technologies社によって、
API設計とドキュメントをちゃんとリンクさせるために作られました。
そこから、RESTful APIを定義するときのデファクトスタンダードになっていったらしいです。

ちなみに、Open APIってこんな感じ。
※ JSONかYAMLで書けるけど、YAMLのがシンプルで見やすくておすすめとのこと。

openapi: 3.0.0
info:
  version: 1.0.0
  title: Sample API
  description: A sample API to illustrate OpenAPI concepts
paths:
  /list:
    get:
      description: Returns a list of stuff              
      responses:
        '200':
          description: Successful response

チュートリアルをやると、
上記に書かれている内容が理解できるようになります!

OpenAPIを使って仕様書つくると何が嬉しいのか?

そもそも、なんでOpenAPI使うといいね!ってなっているんでしょうか。

OpenAPIを使うと、統一されたフォーマットでAPIドキュメントを作成できたり、
OpenAPIに則ったAPIドキュメントから、APIクライアントの自動生成とかができます。

他にも色々なことができたりするみたいです。

ただ、注意点として、APIの仕様を変更したい場合は、OpenAPIを使った仕様書(ymlファイル)の変更とAPIのロジックの変更の2つが生じます。
間違って、APIのロジックだけ変更とかしてしまうと、仕様書の意味がなくなってしまうので気をつけないといけないです。

これらの点を踏まえて、使う使わないの判断をするのが良いと思います。

チュートリアルについて

チュートリアルでは、Open APIの重要ポイントをさらいながら、
シンプルなAPIを1本作っていきます。

【前提知識】

  • RESTful APIに関する基本知識
  • YAMLに関する基本知識(推奨)

SwaggerHub’s built-in editor and validatorを使うと、
書いた仕様書がその場で確認できるのでおすすめです!
https://swagger.io/blog/api-development/new-swaggerhub-editor/

これからAPIを作るにあたって、例を書いていくので、
それをSwagger Editorを使って、自分でも書いてみましょう。

今回は、1から自分で書いていきたいので、
なるべくシンプルなフォーマットでドキュメントを作ります。

登録後の画面で、左側メニュバーの「Create New」ボタン、
または画面中央に表示されている「CREATE API」ボタンをクリックします。

first_swagger_view

モーダルが表示されるので、以下のように記載します。
(なるべく自分で1から書いていきたいので、TemplateはNoneにします)
(VisibilityのPrivateは有料プランでないと使えないらしいです...)
(モックAPIサーバーは一旦使いません。)

で、「Create API」ボタンをクリックすると、
以下のドキュメントが生成されました!

このエディタに追記していくことで、ドキュメントを作っていきましょう。

APIを作る

それでは、ユーザー情報を扱うAPIを作成していきます。
各ユーザーは以下のフィールドを持ってるとします。

  • ユーザー名
  • 氏名
  • ユーザーの居住エリア
  • ユーザーの年齢

APIは、ユーザー情報の取得と、新しいユーザーの登録ができます。

OpenAPIを使ったAPI定義は、主に3つのセクションに分かれてます。

- Meta information
- Path items (endpoints):
	- Parameters
	- Request bodies
	- Responses
- Reusable components:
	- Schemas (data models)
	- Parameters
	- Responses
	- Other components

- メタ情報
- エンドポイント:
	- パラメータ一覧
	- リクエストボディ一覧
	- レスポンス一覧
- 再利用可能なコンポーネント:
	- スキーマ(データモデル)
	- パラメータ一覧
	- レスポンス一覧
	- 他のコンポーネント一覧

へー?という感じだと思うので、詳細を見ていきましょう。

Meta information - メタ情報

メタ情報を書いてみましょう。

メタ情報っていうのは、APIのタイトル、バージョン、サーバーURLとか、
その他もろもろの情報のことです。

APIの具体的な中身じゃなくて、APIに関する情報ってことですね。

Swagger Editorには既に記載されているものもありますが、
以下のように追記しましょう。

openapi: 3.0.0
info:
  version: 1.0
  title: users_api
  description: get and post users

// 以下を追記
servers:
  - url: https://example.io/v1

// pathはメタ情報ではないので、一旦無視してください
path: {}

openapi
1番最初にどのバージョンのOpenAPIの仕様に則って書いてるかを記述します。
今回はOpenAPI3.0に則って書いてるから、openapi: 3.0.0(1行目)となります。

info
APIのタイトル(title)とバージョン(version)は必須項目なので必ず書いてください。
説明書き(description)については、任意です。

servers
ここには、サーバーURLを羅列していきます。

※ サーバーURLは1つのときもあると思うけど、
プロダクションとか、サンドボックスとかのサーバーURLを設定することもあるので、
複数設定できるようになってるみたいです。

このサーバーURLの末尾に、後で出てくるAPIのエンドポイントのパスがくっつく形になります。
今回はhttps://example.io/v1を使います。

※今回は触れないけど、認証まわりも設定できます。
詳しいことについては、以下を読んでみてください。
https://swagger.io/docs/specification/authentication/

TODO: ここまで書いた

Path items - エンドポイント一覧

paths配下に操作したいリソースとHTTPメソッドを記述することで、
APIのエンドポイント一覧を定義できます。

ここで定義するエンドポイントの情報とサーバーURL(今回の例だとhttps://example.io/v1)を合わせて、エンドポイントURLが決まります。

/usersのエンドポイントを作成して、GETメソッドを定義します。
最下部に、以下を追記しましょう。

paths:
  /users:
    get:
     description: ユーザー一覧を取得します

これで、GET https://example.io/v1/usersのエンドポイントを追加できました。

Responses - レスポンス

次はレスポンスの情報を追記します。

レスポンスには、必ずHTTPステータスコードを含みます。
※HTTPステータスコードについては、以下記事が参考になります。
https://www.restapitutorial.com/httpstatuscodes.html

リクエストが成功したらユーザー一覧を返し、
失敗したらエラーメッセージを返す、という定義をしましょう。

以下を追記してください。

paths:
  /users:
    get:
      description: ユーザー一覧を取得します
      #  ----- ここから下を追加  ----------------------------------------
      responses:
        '200':
          description: ユーザー一覧を返す
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  required:
                    - username
                  properties:
		    username:
                      type: string
                    name:
                      type: string
                    area:
                      type: string
                    age:
                      type: integer

        '400':
          description: 不正なリクエスト
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string
      #  ---- /ここから上を追加  ----------------------------------------

これで、リクエストが成功した場合の200ステータスのレスポンスと
失敗した場合の400ステータスのレスポンスを定義できました。

※レスポンスの記述については、以下の記事で紹介しています。
https://swagger.io/docs/specification/describing-responses/

Parameters - パラメーター

エンドポイントで設定するパラメータについて説明していきます。

※パラメータに関するより詳細な情報は以下記事を参照してください。
https://swagger.io/docs/specification/describing-parameters/

Query parameters - クエリパラメーター

クエリパラメータは、よく使われるものの1つですよね。
URL末尾のクエスチョンマーク以降に記述されます。

今回の例では、ユーザー全件を取得してくるのではなく、指定した件数だけ返すようにします。
表示件数(limit)を指定できるようにしましょう。
例:GET https://example.io/v1/artists?limit=20

以下を追記してください。

paths:
  /users:
    get:
      description: ユーザー一覧を取得します
      #  ----- ここから下を追加  ----------------------------------------
      parameters:
        - name: limit
          in: query
          description: 表示件数を制限します
          schema:
            type: integer
      #  ---- /ここから上を追加  ----------------------------------------       
      
      responses:
        '200':
          description: ユーザー一覧を返す
          content:
            ...

        '400':
          description: 不正なリクエスト
          content:
            ...

parameters配下に、パラメータの情報を記載し、
クエリパラメータを定義できました。

Request body - リクエストボディ

POST, PUT, PATCHメソッドのリクエストは通常リクエストボティを必要とします。
リクエストボディは、requestBody配下に定義します。

ユーザー情報をPOSTで送信できるようにしましょう。
/users配下に、POSTメソッドの記述をします。

getと同じ階層に、以下を追記してください。

paths:
  /users:
    get:
      ...

    #  ----- ここから下を追加  ----------------------------------------
    post:
      description: ユーザーの登録
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object 
              required:
                - username
              properties:
                username:
                  type: string
                name:
                  type: string
                area:
                  type: string
                age:
                  type: integer

      responses:
        '200':
          description: ユーザー登録成功

        '400':
          description: 不正なリクエスト
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string
    #  ---- /ここから上を追加  ----------------------------------------

Path parameters - パスパラメーター

パスパラメーターは、特定のデータにアクセスするときなどに使いますよね。
例:https://example.io/v1/users/{username}

パスパラメーターを使う際は、書く順番に気をつけましょう。
書いた順に、パスに反映されます。
/users/{companyId}/{userId}だったら、companyIdの方を先に記述する)

それでは、特定のユーザー情報を返すエンドポイントを作成しましょう。
特定のユーザーを取得するにはusernameが必要なので、GETメソッドの配下のparametersの下に、今回のパスパラメータであるusernameを記述します。

paths:
  /users:
    get:
      description: ユーザー一覧を取得します
        ...
    post:
      description: Lets a user post a new artist
        ...

  #  ----- ここから下を追加  ----------------------------------------
  /users/{username}:
    get:
      description: ユーザーネームをもとに、ユーザー情報を取得する
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          
      responses:
        '200':
          description: ユーザー取得成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  username:
                    type: string
                  name:
                    type: string
                  area:
                    type: string
                  age:
                    type: integer
                
        '400':
          description: 不正なリクエスト
          content:
            application/json:
              schema:
                type: object 
                properties:           
                  message:
                    type: string
  #  ---- /ここから上を追加  ----------------------------------------

はい!これでパスパラメーターにusernameを指定できました。
必須パラメーターとしているので、呼び出すときは必ず値をセットするようにしてください。

おめでとうございます!
これで、ユーザーを取得してくるAPIの仕様書が作成できました!

Reusable components - 再利用可能なコンポーネント

ここまでで定義したのはたった2つのエンドポイントと3つのアクションだけです。
これは130行の定義になったわけですが、APIの規模が大きくなるにつれてボリュームも増えます。

ここで1つ気づく点としては、ユーザーのスキーマ(ユーザーネーム、氏名、居住エリア、年齢)が繰り返し出てきていることがあります。
なので1度定義したら、それを使いまわせるようにしたい!できます!

それが再利用可能なコンポーネントです。
複数のエンドポイントにまたがって利用できます。

components配下に定義し、各エンドポイントで呼び出します。

ちなみに色んなもの(例えば以下)を再利用可能なコンポーネントにできます。

  • Schemas (data models)
  • Parameters
  • Request bodies
  • Responses
  • Response headers
  • Examples
  • Links
  • Callbacks

Schemas - スキーマ

Componentsの下のSchemas配下には、APIで使われるデータモデルを記述できます。

それでは、200ステータスのレスポンスで使うスキーマをコンポーネント化していきます。
以下を追記してください。

paths:
  /users:
    get:
      description: ユーザー一覧を取得します
      parameters:
        - name: limit
          in: query
          description: Limits the number of items on a page
          schema:
            type: integer
        - name: offset
          in: query
          description: Specifies the page number of the artists to be displayed
          schema:
            type: integer
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  #  ----- 書き換え  ----------------------------------------
                  $ref: '#/components/schemas/Artist'
                  #  ---- /書き換え  ----------------------------------------
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

    post:
      description: Lets a user post a new artist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              #  ----- 書き換え  ----------------------------------------
              $ref: '#/components/schemas/Artist'
              #  ---- /書き換え  ----------------------------------------
      responses:
        '200':
          description: Successfully created a new artist
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                type: object
                properties:   
                  message:
                    type: string

  /users/{username}:
    get:
      description: Obtain information about an artist from his or her unique username
        ...

#  ----- ここから下を追加  ----------------------------------------             components: 
  schemas:
    User:
      type: object
      required:
        - username
      properties:
        username:
          type: string
        name:
          type: string
        area:
          type: string
        age:
          type: integer
#  ---- /ここから上を追加  ----------------------------------------

componentsにスキーマが追加されて、もともとスキーマが書いてあった箇所には、
そのスキーマのリファレンスが記載しました。

定義全体が短くなっただけでなく、
新たにエンドポイントを作るときに再定義する手間がなくなります。

Componentsの詳細については以下の記事を参照してください。
https://swagger.io/docs/specification/components/

Parameters and Responses - パラメータとレスポンス

componentsセクションには、再利用可能なパラメータやレスポンスも定義することができます。

以下の例では、limitのクエリパラメータをコンポーネント化し、/usersのエンドポイントで参照するようにしています。
また、400エラーのレスポンスもコンポーネント化し、各エンドポイントから参照できるようにしています。

openapi: 3.0.0
info:
  version: 1.0.0
  title: Simple API
  description: A simple API to illustrate OpenAPI concepts

servers:
  - url: https://example.io/v1

security:
  - BasicAuth: []

paths:
  /artists:
    get:
      description: Returns a list of artists 
      parameters:
        #  ----- Added line  ------------------------------------------
        - $ref: '#/components/parameters/PageLimit'
        - $ref: '#/components/parameters/PageOffset'
        #  ---- /Added line  ------------------------------------------
      responses:
        '200':
          description: Successfully returned a list of artists
          content:
            application/json:
              schema:
                type: array
                items:
                  #  ----- Added line  --------------------------------
                  $ref: '#/components/schemas/Artist'
                  #  ---- /Added line  --------------------------------
        '400':
          #  ----- Added line  ----------------------------------------
          $ref: '#/components/responses/400Error'
          #  ---- /Added line  ----------------------------------------

    post:
      description: Lets a user post a new artist
      requestBody:
        required: true
        content:
          application/json:
            schema:
              #  ----- Added line  ------------------------------------
              $ref: '#/components/schemas/Artist'
              #  ---- /Added line  ------------------------------------
      responses:
        '200':
          description: Successfully created a new artist
        '400':
          #  ----- Added line  ----------------------------------------
          $ref: '#/components/responses/400Error'
          #  ---- /Added line  ----------------------------------------    

  /artists/{username}:
    get:
      description: Obtain information about an artist from his or her unique username
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          
      responses:
        '200':
          description: Successfully returned an artist
          content:
            application/json:
              schema:
                type: object
                properties:
                  artist_name:
                    type: string
                  artist_genre:
                    type: string
                  albums_recorded:
                    type: integer
                
        '400':
          #  ----- Added line  ----------------------------------------
          $ref: '#/components/responses/400Error'
          #  ---- /Added line  ----------------------------------------     

components:
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic

  schemas:
    Artist:
      type: object
      required:
        - username
      properties:
        artist_name:
          type: string
        artist_genre:
            type: string
        albums_recorded:
            type: integer
        username:
            type: string

  #  ----- Added lines  ----------------------------------------
  parameters:
    PageLimit:
      name: limit
      in: query
      description: Limits the number of items on a page
      schema:
        type: integer
      
    PageOffset:
      name: offset
      in: query
      description: Specifies the page number of the artists to be displayed
      schema:
        type: integer

  responses:
    400Error:
      description: Invalid request
      content:
        application/json:
          schema:
            type: object 
            properties:
              message:
                type: string
  #  ---- /Added lines  ----------------------------------------

定義に飛ぶには、$refのリンクをクリックしてください。

(自分がクリックした感じ、飛ばなかった気がする...)

サマリ

これで、アーティスト情報を扱うRESTful APIを定義することができました!
今回はOpenAPIの基本をさらっただけですが、他にも色んなことができます。

もっと詳しく知りたい人は、以下の記事を参考にしてみてください!

https://swagger.io/specification/
https://swagger.io/docs/specification/basic-structure/

また、certificationコースもあるので、ぜひ試してみてくださいね!
https://smartbear.com/academy/

個人的なまとめ

このTutorialめちゃわかりやすかったです。

ざっとやってみたら、基本的なことがわかったし、
実際にSwagger EditorでドキュメントをHTML表示させられるので良い感じでした。

OpenAPIについて、自分が色々調べて分かったこととしては、以下になります。

  • OpenAPIとは、APIの定義を書くときの記述フォーマットのこと
  • OpenAPI使って仕様定義するとAPIドキュメントが作れる
    • フォーマットが決まってるから、書き方も統一される
    • Swagger Editorとか使えばHTMLで見れる
  • (補足)その他ツールを使うと、ドキュメントをもとにコード生成とかできたりする

業務で使ったり自分で試したりする過程で、色んなことを調べたので
そこもアウトプットしていけたらなと思います。

Discussion