コンテンツにスキップ

API仕様書@RESTful-API

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. OpenAPI仕様

OpenAPI仕様とは

RESTful-APIの仕様を実装により説明するためのフォーマットのこと。JSON型またはYAML型で実装できる。

いくつかのフィールドから構成されている。

openapi: # openapiフィールド

info: # infoフィールド

servers: # serversフィールド

paths: # pathsフィールド

webhooks: # webhooksフィールド

components: # componentsフィールド

security: # securityフィールド

tags: # tagsフィールド

externalDocs: # externalDocsフィールド


OpenAPI仕様の視覚化

▼ OpenAPI仕様の視覚化とは

OpenAPI仕様を共有して閲覧できるように、仕様書の静的ファイル (例:htmlファイル、cssファイル、jsファイル) を自動的に作成する。

さらに、静的なWebサイトとして公開すると、チーム内で共有するとなおよい。

▼ ReDocの場合

ReDocを使用して、OpenAPI仕様から静的ファイルを作成する。

openapi: 3.0.0
info:
  version: 1.0.0
  title: Swagger sample
paths:
  /users:
    get:
      summary: Usersを取得するAPIです。
      responses:
        "200":
          description: HTTPステータスコード200と以下の情報を含むJSONオブジェクトを返します。
          content:
            application/json:
              schema:
                type: object
                properties:
                  user_id:
                    type: string
                    example: 1234567
$ npm install -g redoc-cli

$ redoc-cli bundle openapi.yaml redoc.html

▼ Swagger UI / Swagger Codegenの場合

Swagger UI / Swagger Codegenを使用して、OpenAPI仕様から静的ファイルを作成する。

▼ OpenAPI Generator

openapi-generatorを使用して、OpenAPI仕様から静的ファイルを作成する。

なお、openapi-generatorはOpenAPI仕様からコードを作成することもできる。

$ openapi-generator generate -i docs/openapi.yaml -o docs/html -g html


スキーマ駆動開発

▼ スキーマ駆動開発とは

以下の流れに基づいて、APIを実装していく。

ここでは、コードの自動作成にOpenAPI Generatorを使用するものとする。

(1)

OpenAPIに基づいてAPI仕様を定義する。

openapi: 3.0.3
info:
  description: EC API
  title: EC
  version: 2023.1.1
servers:
  - description: Online EC Example Service
    url: http://example.com/v1
tags:
  - description: Product Operation
    name: Product
paths:
  /products:
    get:

    ...

  /products/{productId}:
    get:

    ...


components:
  schemas:

  ...
(2)

OpenAPI Generatorを使用して、API仕様から静的ファイルを自動的に作成する。

$ openapi-generator generate -i docs/openapi.yaml -o docs/html -g html
(3)

OpenAPI Generatorを使って、API仕様からコードを自動的に作成する。

# brew、npmなどでインストールする
$ brew install openapi-generator

# GoのAPIを作成する
# openapi-generator generate -i <OpenAPI仕様書> -g <言語、フレームワーク> -p packageVersion=<タグ> -o <出力先ディレクトリ>
$ openapi-generator generate -i docs/openapi.yaml -g go-gin-server -p packageVersion=0.0.1 -o go-gin

# --template-dirを使用すると、ユーザー定義のテンプレートからコードを作成できる
// テンプレートファイル (mustache形式)
{{>partial_header}}
package main

import (
    "log"
    // WARNING!
    // Change this to a fully-qualified import path
    // once you place this file into your project.
    // For example,
    //
    //    sw "github.com/myname/myrepo/{{apiPath}}"
    //
    sw "openapi-gin-server/{{apiPath}}"
)

func main() {

    log.Printf("Server started")

    router := sw.NewRouter()

    log.Fatal(router.Run(":{{serverPort}}"))
}
(3)

自動的に作成されたコードに実際のAPIを追加実装していく。

/*
 * EC
 *
 * EC API
 *
 * API version: 2023.1.1
 * Generated by: OpenAPI Generator (https://openapi-generator.tech)
 */

package openapi

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

// DeleteProductById - Cancel a specified product
func DeleteProductById(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{})
}

// GetProductById - Get a specified product
func GetProductById(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{})
}

// GetProducts - Get all products
func GetProducts(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{})
}


クラウドプロバイダーとの統合

▼ API Gatewayの場合

API GatewayによるOpenAPI仕様のインポートについては、以下のリンクを参考にせよ。


02. フィールド

openapiフィールド (必須)

OpenAPI仕様のバージョンを定義する。

*実装例*

openapi: 3.0.0


infoフィールド (必須)

API名、作成者名、メールアドレス、ライセンスなどを定義する。

*実装例*

info:
  title: Foo API # API名
  description: The API for Foo. # APIの説明
  termsOfService: https://www.foo.com/terms/ # 利用規約
  contact:
    name: API support # 連絡先名
    url: https://www.foo.com/support # 連絡先に関するURL
    email: support@foo.com # メールアドレス
  license:
    name: Apache 2.0 # ライセンス
    url: https://www.apache.org/licenses/LICENSE-2.0.html # URL
  version: <バージョンタグ> # APIドキュメントのバージョン


serversフィールド

API自体のURLなどを定義する。

*実装例*

servers:
  - url: https://{env}.foo.com/api/v1
    description: |
    variables:
      env:
        default: stg
        description: API environment
        enum:
          - stg
          - www


pathsフィールド (必須)

APIのエンドポイント、HTTPメソッド、ステータスコードなどを定義する。

paths:
  #===========================
  # pathsオブジェクト
  #===========================
  /users:
    #===========================
    # path itemオブジェクト
    #===========================
    get: # GETメソッドを設定する。
      tags:
        - ユーザー情報取得エンドポイント
      summary: ユーザー情報取得
      description: 全ユーザー情報を取得する。
      #===========================
      # リクエスト
      #===========================
      parameters: []
      #===========================
      # レスポンス
      #===========================
      responses:
        "200":
          description: OK レスポンス
          content:
            application/json: # MIME type
              foo: # レスポンスボディ例
                Users:
                  User:
                    userId: 1
                    name: Hiroki
              schema:
                $ref: "#/components/schemas/user" # Userモデルを参照する。
        "400":
          description: Bad Request レスポンス
          content:
            application/json: # MIME type
              foo: # レスポンスボディ例
                status: 400
                title: Bad Request
                errors:
                messages: ["不正なリクエストです。"]
              schema:
                $ref: "#/components/schemas/error" # 異常系モデルを参照する。
        "401":
          $ref: "#/components/responses/unauthorized" # 認証エラーを参照する。
    #===========================
    # path itemオブジェクト
    #===========================
    post: # POSTメソッドを設定する。
      tags:
        - ユーザー情報作成エンドポイント
      summary: ユーザー情報作成
      description: ユーザー情報を作成する。
      #===========================
      # リクエスト
      #===========================
      parameters: []
      requestBody: # メッセージボディにパラメーターを割り当てる。
        description: ユーザーID
        content:
          application/json: # MIME type
            foo: # メッセージボディ例
              userId: 1
            schema: # スキーマ
              $ref: "#/components/schemas/user" # Userモデルを参照する。
      #===========================
      # レスポンス
      #===========================
      responses:
        "200":
          description: OK レスポンス
          content:
            application/json: # MIME type
              foo: # レスポンスボディ例
                userId: 1
              schema:
                $ref: "#/components/schemas/normal" # スキーマとして、正常系モデルを参照する。
        "400":
          description: Bad Request レスポンス
          content:
            application/json: # MIME type
              foo: # レスポンスボディ例
                status: 400
                title: Bad Request
                errors:
                  messages: ["ユーザーIDは必ず指定してください。"]
              schema:
                $ref: "#/components/schemas/error" # スキーマとして、異常系モデルを参照する。
        "401":
          $ref: "#/components/responses/unauthorized" # 認証エラーを参照する。
  #===========================
  # pathsオブジェクト
  #===========================
  /users/{userId}:
    #===========================
    # path itemオブジェクト
    #===========================
    get:
      tags:
        - ユーザー情報取得エンドポイント
      summary: 指定ユーザー情報取得
      description: 指定したユーザー情報を取得する。
      #===========================
      # リクエスト
      #===========================
      parameters:
        - in: path # パスにパラメーターを割り当てる。
          name: userId
          required: "true"
          description: ユーザーID
          schema:
            type: string
            foo: # パスパラメーター例
              userId=1
      #===========================
      # レスポンス
      #===========================
      responses:
        "200":
          description: OK レスポンス
          content:
            application/json: # MIME type
              foo: # ボディ例
                userId: 1
                name: Hiroki
              schema: # スキーマ
                $ref: "#/components/schemas/user" # Userモデルを参照する。
        "400":
          description: Bad Request レスポンス
          content:
            application/json: # MIME type
              foo: # ボディ例
                status: 400
                title: Bad Request
                errors:
                  messages: ["ユーザーIDは必ず指定してください。"]
              schema:
                $ref: "#/components/schemas/error" # 異常系モデルを参照する。
        "401":
          $ref: "#/components/responses/unauthorized" # 認証エラーを参照する。
        "404":
          description: Not Found レスポンス
          content:
            application/json: # MIME type
              foo: # ボディ例
                status: 404
                title: Not Found
                errors:
                  messages: ["対象のユーザーが見つかりませんでした。"]
              schema:
                $ref: "#/components/schemas/error" # 異常系モデルを参照する。
    #===========================
    # path itemオブジェクト
    #===========================
    put:
      tags:
        - ユーザー情報更新エンドポイント
      summary: 指定ユーザー更新
      description: 指定したユーザー情報を更新する。
      #===========================
      # リクエスト
      #===========================
      parameters:
        - in: path # パスにパラメーターを割り当てる。
          name: userId
          required: "true"
          description: ユーザーID
          schema:
            type: string
            foo: # パスパラメーター例
              userId=1
      #===========================
      # レスポンス
      #===========================
      responses:
        "200":
          description: OK レスポンス
          content:
            application/json: # Content-Type
              foo: # ボディ例
                userId: 1
                name: Hiroki
              schema: # スキーマ
                $ref: "#/components/schemas/user" # Userモデルを参照する。
        "400":
          description: Bad Request レスポンス
          content:
            application/json: # Content-Type
              foo: # ボディ例
                status: 400
                title: Bad Request
                errors:
                  messages: ["ユーザーIDは必ず指定してください。"]
              schema:
                $ref: "#/components/schemas/error" # 異常系モデルを参照する。
        "401":
          $ref: "#/components/responses/unauthorized" # 認証エラーを参照する。
        "404":
          description: Not Found レスポンス
          content:
            application/json: # Content-Type
              foo: # ボディ例
                status: 404
                title: Not Found
                errors:
                  messages: ["対象のユーザーが見つかりませんでした。"]
              schema:
                $ref: "#/components/schemas/error" # 異常系モデルを参照する。


componentsフィールド (必須)

スキーマなど、他の項目で共通して利用するものを定義する。

components:
  #===========================
  # callbackキーの共通化
  #===========================
  callbacks: {}
  #===========================
  # linkキーの共通化
  #===========================
  links: {}
  #===========================
  # responseキーの共通化
  #===========================
  responses:
    unauthorized:
      description: Unauthorized レスポンス
      content:
        application/json: # MIME type
          foo: # ボディ例
            status: 401
            title: Unauthorized
            errors:
              messages: ["APIキーの認可に失敗しました。"]
          schema:
            $ref: "#/components/schemas/error" # 異常系モデルを参照する。
  #===========================
  # schemaキーの共通化
  #===========================
  schemas:
    # ユーザー
    user:
      type: object
      properties:
        userId:
          type: string
        name:
          type: string
    # 正常系
    normal:
      type: object
      properties:
        userId:
          type: string
    # 異常系
    error:
      type: object
      properties:
        messages:
          type: array
          items:
            type: string
  #===========================
  # securityフィールドの共通化
  #===========================
  securitySchemes:
    # Basic認証
    basicAuth:
      description: Basic認証
      type: http
      scheme: basic
    # Bearer認証
    bearerAuth:
      description: Bearer認証
      type: http
      scheme: bearer
    # APIキー認証
    apiKeyAuth:
      description: APIキー認証
      type: apiKey
      name: x-api-key # ヘッダ名は『x-api-key』とする。小文字が推奨である。
      in: header


securityフィールド

componentsフィールドで定義した認証方法を宣言する。

ルートで宣言すると、全てのパスに適用できる。

*実装例*

security:
  - apiKeyAuth: []


tagsフィールド

各項目に付けるタグを定義する。

同名のタグをつけると、自動的にまとめられる。

*実装例*

tags:
  - name: ユーザー情報取得エンドポイント
    description: |

      ...


externalDocsフィールド

APIを説明するドキュメントのリンクを定義する。

*実装例*

externalDocs:
  description: 補足情報はこちら
  url: https://foo.com


03. スキーマ

スキーマとは

RESTful-APIが受信するCRUDのデータ型や必須データを定義したもの。受信したデータのバリデーションに使用する。


スキーマによるバリデーション

データ型や必須データにより、リクエスト/レスポンスのデータのバリデーションを実行する。

*実装例*

例えば、APIがレスポンス時に以下のようなJSON型データを返信する例を考える。

{
  "id": 1,
  "name": "Taro Yamada",
  "age": 10,
  "sports": ["soccer", "baseball"],
  "subjects": "math",
}

ここで、スキーマを以下の様に定義しておき、APIからデータをレスポンスする時のバリデーションを実行する。

{
  "$schema": "https://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties":
    {
      "id": {"type": "integer", "minimum": 1},
      "name": {"type": "string"},
      "age": {"type": "integer", "minimum": 0},
      "sports": {"type": "array", "items": {"type": "string"}},
      "subjects": {"type": "string"},
    },
  "required": ["id"],
}