ヒトリ歩き

愚痴とかいろいろ書きます

OpenAPIでREST APIを設計する

REST APIを設計する機会が増えてきたこともあり、Open APIを使って設計を試しにやってみました。

作成するAPIネタ

REST APIを設計するにあたり、APIのネタは「TODO」に関するものにしました。
本当に必要最低限の情報だけとします。
よって、以下のAPIを設計します。

  • TODOの作成
  • TODOの更新
  • TODOの取得
  • TODOの削除

OpenAPIとは何ですか

REST APIを設計する前に、OpenAPIとは何かを簡単に書きます。

OpenAPI仕様は、REST APIのためのAPI記述フォーマット。
OpenAPIファイルは、以下の記述をすることが可能です。

・利用可能なエンドポイントと各エンドポイントに対する操作
・各操作パラメータの入出力の定義
・認証方法
・情報、ライセンス、ユーザやその他の情報

API仕様は、YAMLJSONで記述することができる。
ツールとしては、Swagger Editor、Swagger UI、Swagger Codegenがありますが、そのほかにもOpenAPIでの記法をサポートしてくれるツール群は存在します。

Swagger Editorを起動してみる

OpenAPIでREST APIを設計するためにSwagger Editorを使って作業を進めることにします。
Swagger Editorを使うにはDockerから起動するかHTTPサーバを起動するかのどちらかだと思います。
(他のやり方があるかもしれませんが、私が知る限りは2パターン)

Dockerで起動する

Dockerを使ってSwagger Editorを起動する場合は以下の手順で実行します。

docker pull swaggerapi/swagger-editor
docker run -d -p 80:8080 swaggerapi/swagger-editor

HTTPサーバで起動する

HTTPサーバで起動する場合、私はNode.jsをインストールしhttp-serverを起動して使用しています。

git clone https://github.com/swagger-api/swagger-editor.git
npm install http-server
cd swagger-editor
../node_module/.bin/http-server

REST APIを設計してみた

先ほどTODOに関する操作のREST APIを設計してみました。
OpenAPIの記法などはOpenAPIのページを見るのが一番良いと思います。
APIに関する情報を記載するinfoセクションなどの記載内容がよく分かってないので、ちゃんと調べようと思いました。
実際のAPIの部分については、最初はどのように記述したら良いか分からなかったのですが、Swagger Editor が記法に誤りがあればお知らせしてくれるので、すぐに記述誤りに気づくことができます。
また、なぜ間違っているのかの情報とどこの行番号に間違いが発生しているのか分かります。

swagger.io

github.com

openapi: 3.0.3

info:
  title: TODO api
  description: TODOを操作するためのAPI
  termsOfService: http://example.com/terms/
  contact:
    name: 窓口担当
    url: http://www.todomng.com
    email: todommng@example.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
  version: 1.0.0

servers:
  - url: http://localhost
    description: todo api server

paths:
  /todo:
    post:
      tags:
        - TODOの登録
      description: TODOを登録する
      requestBody:
        content:
          application/json:
           schema:
            $ref: '#/components/schemas/TodoInformation'
      responses:
        '201':
          description: TODOの登録完了
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoId'
        '400':
          description: リクエスト不正
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'
        '500':
          description: システム内で異常が発生し正常にリクエストが処理できなかった場合
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'

    get:
      tags:
        - TODOの取得
      description: TODOの一覧を取得する
      parameters:
        - in: query
          name: todo_id
          schema:
            type: integer
            format: int32
        - in: query
          name: offset
          schema:
            type: integer
            format: int32
        - in: query
          name: limit
          schema:
            type: integer
            format: int32
      responses:
        '200':
          description: TODO情報
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/TodoInformation'
        '400':
          description: リクエスト不正
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'
        '500':
          description: システム内で異常が発生し正常にリクエストが処理できなかった場合
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'

  '/todo/{todo_id}':
    patch:
      parameters:
        - in: path
          required: true
          name: todo_id
          schema:
            type: integer
            format: int32
      tags:
        - TODOを更新
      description: TODOを更新する
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TodoInformation'
      responses:
        '204':
          description: TODO更新成功
        '400':
          description: リクエスト不正
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'
        '404':
          description: 更新対象のリソースが存在しない
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'
        '500':
          description: システム内で異常が発生し正常にリクエストが処理できなかった場合
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'


    delete:
      parameters:
        - in: path
          required: true
          name: todo_id
          schema:
            type: integer
            format: int32
      tags:
        - TODOを削除
      description: TODOを削除する
      responses:
        '200':
          description: TODOの削除成功
        '400':
          description: リクエスト不正
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'
        '404':
          description: 削除対象のリソースが存在しない
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'
        '500':
          description: システム内で異常が発生し正常にリクエストが処理できなかった場合
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TodoOpeError'

components:
  schemas:
    TodoInformation:
      type: object
      properties:
        title:
          type: string
          minLength: 1
          maxLength: 100
          description: TODOの件名
          example: todo追加
        detail:
          type: string
          maxLength: 300
          description: TODOの詳細
          example: todoを追加します
        progress:
          type: integer
          format: int32
          minimum: 0
          maximum: 100
          description: TODOの進捗率
          example: 50
        start_date:
          type: string
          format: date
          description: 開始予定日
        end_date:
          type: string
          format: date
          description: 終了予定日
      required:
        - title
        - progress

    TodoId:
      type: object
      properties:
        todo_id:
          type: integer
          format: int32
          description: TODOの登録番号
          example: 1

    TodoOpeError:
      type: object
      properties:
        error_code:
          type: string
          description: エラーコード
          example: 0000001
        error_message:
          type: string
          description: エラーメッセージ
          example: error message.

まとめ

REST APIの設計にエクセルを使用していたこともありましたが、エクセルだと記法の誤りを検出するには、人頼りになるところがあるため、設計バグを盛り込んでしまう可能性があります。
それに対し、OpenAPIによる設計かつSwagger Editorを活用することで記法の誤りは事前に防ぐことができます。
記法の誤りを事前に検出できれば、バグの盛り込みを未然に防ぐことが可能です。
そのため、REST APIを設計する際は必ずOpenAPIで定義するようにしようと思いました。
また、設計するにはどのように定義すれば良いのか調べる時間も必要のため、最初の設計時には多少の作業コストがかかることを事前に見積もっておくことが大事だと思います。

最後に

今回、OpenAPIは3.0.3を指定してましたが、3.1.0がリリースされてました。
3.1.0でどのような記述が可能になったのか、確認したい。

github.com

3.1.0の紹介がYoutubeにあがっている。
www.youtube.com

ちゃんとステータスコードも設計しよう。
developer.ntt.com