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"],
}