ほとんどのAPIバグはコーディングミスではなく、契約の不一致です。フロントエンドは userId を期待しているのに、バックエンドは user_id を返す。このような差分はQAや結合テストまで見つからないことがあります。スペックファーストAPI開発では、実装前にOpenAPIスペックを作成し、チーム全員が同じ契約に基づいて開発します。
このガイドでは、小さな /users APIを例に、OpenAPIスペックを手作業で作成し、その1つのファイルからモック、テスト、ドキュメントを生成する流れを整理します。このアプローチは、スペック駆動開発、デザインファースト、契約ファーストとも呼ばれます。共通する考え方は、先にインターフェースへ合意し、その後に実装することです。
スペックファーストAPI開発とは
スペックファーストAPI開発では、エンドポイントを実装する前に、OpenAPIなどの機械可読な契約を作成します。
この契約には、次の内容を明示します。
- パス
- HTTPメソッド
- クエリパラメータ
- リクエストボディ
- レスポンス形式
- ステータスコード
- 共通スキーマ
- 必須フィールド
- データ型とフォーマット
スペックは、コードの後に書く補助ドキュメントではありません。開発の起点です。
実装前にスペックがあると、各チームは並行して作業できます。
- フロントエンド: スペックから生成されたモックAPIに対して実装する
- QA: スペックを元に契約テストを作成する
- バックエンド: スペックを満たすように実装する
- ドキュメント: 同じスペックからAPIリファレンスを生成する
コードファーストでは、実装後にドキュメントを書き、そこで不一致が見つかることがあります。スペックファーストでは、先に契約をレビューするため、統合時の手戻りを減らせます。
スペックファーストとコードファーストのライフサイクル
2つのアプローチは、最終的には同じAPIを作れます。違いは、問題が見つかるタイミングと、誰がいつ作業を開始できるかです。
スペックファーストでは、契約が最初に存在します。そのため、フロントエンド、バックエンド、QAが互いを待たず、同じ定義に基づいて作業を開始できます。
OpenAPIの作業例
ここでは、/users エンドポイントをOpenAPIで設計します。
作成するAPIは次の2つです。
-
GET /users: ユーザー一覧を取得する -
POST /users: ユーザーを作成する
1. OpenAPIドキュメントのヘッダーを定義する
まず、OpenAPIのバージョン、API名、バージョン、サーバーURLを定義します。
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
servers:
- url: https://api.example.com/v1
この時点ではエンドポイントはまだありません。API全体のメタデータだけを定義しています。
2. 共通のUserスキーマを定義する
次に、components.schemas に User スキーマを作成します。
components:
schemas:
User:
type: object
required:
- id
- email
- createdAt
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
createdAt:
type: string
format: date-time
ポイントは、レスポンスごとに同じ構造をコピーしないことです。
共通スキーマを components に置き、各エンドポイントでは $ref で参照します。これにより、フィールド名や型のズレを防げます。
3. GET /usersを定義する
GET /users は、ユーザー一覧を返すエンドポイントです。
ここでは、limit クエリパラメータを受け取り、最大値を 100 に制限します。
paths:
/users:
get:
summary: List users
operationId: listUsers
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
"200":
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
items で #/components/schemas/User を参照しているため、レスポンスは User オブジェクトの配列になります。
4. POST /usersを定義する
次に、ユーザー作成用の POST /users を追加します。
paths:
/users:
post:
summary: Create a user
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
properties:
email:
type: string
format: email
name:
type: string
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: Invalid request body
この定義で、クライアントが送信すべきリクエストボディと、成功時・失敗時のレスポンスが明確になります。
5. 完全なOpenAPIファイル
上記を1つにまとめると、次のようになります。
openapi: 3.0.3
info:
title: Users API
version: 1.0.0
servers:
- url: https://api.example.com/v1
paths:
/users:
get:
summary: List users
operationId: listUsers
parameters:
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
responses:
"200":
description: A list of users
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
post:
summary: Create a user
operationId: createUser
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
properties:
email:
type: string
format: email
name:
type: string
responses:
"201":
description: User created
content:
application/json:
schema:
$ref: "#/components/schemas/User"
"400":
description: Invalid request body
components:
schemas:
User:
type: object
required:
- id
- email
- createdAt
properties:
id:
type: string
format: uuid
email:
type: string
format: email
name:
type: string
createdAt:
type: string
format: date-time
この時点で、サーバーコードはまだありません。しかし、API契約としては次の内容が確定しています。
-
emailは必須 -
idはUUID -
createdAtは日時 -
limitは最大100 - 作成成功時は
201 - 不正なリクエストは
400 - ユーザーオブジェクトの形式は共通スキーマで管理する
スペックからモック、テスト、ドキュメントを生成する
スペックファーストの実用的なメリットは、1つのOpenAPIファイルから複数の成果物を生成できることです。
モックを生成する
モックサーバーはOpenAPIスペックを読み取り、スキーマに合うサンプルレスポンスを返します。
たとえば、GET /users のレスポンススキーマには次の情報があります。
type: array
items:
$ref: "#/components/schemas/User"
User スキーマには、さらに次のフォーマット指定があります。
id:
type: string
format: uuid
email:
type: string
format: email
createdAt:
type: string
format: date-time
このため、ツールはUUID、メールアドレス、日時として妥当なサンプルデータを生成できます。
フロントエンドは、バックエンド実装が完了する前に、モックAPIへリクエストして画面実装を進められます。
契約テストを作成する
スペックには、期待されるレスポンス形式とステータスコードが定義されています。
そのため、実装後は次のような観点でテストできます。
-
POST /usersが成功時に201を返す - レスポンスボディが
Userスキーマに一致する -
emailが欠落したリクエストで400を返す -
GET /usersのレスポンスが配列である - 各要素が
Userスキーマに一致する
これは、テスト仕様をゼロから考えるのではなく、合意済みの契約を検証する作業です。
ドキュメントを生成する
APIリファレンスもOpenAPIスペックから生成できます。
ドキュメントに表示される次の情報は、同じYAMLから作られます。
- エンドポイント一覧
- メソッド
- パラメータ
- リクエストボディ
- レスポンス例
- ステータスコード
- スキーマ定義
手作業で別ドキュメントを管理しないため、実装・テスト・ドキュメントのずれを減らせます。
この考え方は、GitネイティブAPIワークフローとも相性が良いです。スペックはプレーンテキストなので、API契約への変更はプルリクエストの差分としてレビューできます。
レビュー担当者は、たとえば次のような変更を出荷前に確認できます。
-
emailが別名に変わっていないか - 必須フィールドが削除されていないか
- ステータスコードが変わっていないか
- レスポンススキーマに破壊的変更がないか
Apidogでの実施
Apidogは、スペックファーストモードを通じて、このワークフローをサポートします。
OpenAPIファイルを単なるエクスポート結果として扱うのではなく、プロジェクトの中心として扱います。YAMLを編集すると、モック、ドキュメント、テストの作業もその契約に基づいて進められます。
実装の流れは次のようになります。
-
/usersのOpenAPIスペックを作成する - ApidogのエディタにYAMLを貼り付ける、または直接編集する
- スペックを解析してエンドポイントを確認する
- モックサーバーを起動する
- フロントエンドからモックAPIを呼び出す
- 生成されたドキュメントを確認する
- バックエンド実装後、実レスポンスがスキーマに一致するか検証する
- スペック変更をGitでレビューする
双方向のGit同期を使うと、スペックをリポジトリに置いたまま管理できます。エディタでYAMLを編集してプッシュするとApidog側に反映され、Apidogでの編集もチームがレビューできる変更として扱えます。
スペックファーストとデザインファーストの位置づけを比較したい場合は、Apidogにおけるスペックファースト vs デザインファーストも参照してください。
スペックファーストチェックリスト
スペックを実装に進めてよい状態か確認するには、次のチェックリストを使います。
- OpenAPIスキーマとしてエラーなく検証できる
- すべてのエンドポイントに成功レスポンスがある
- すべてのエンドポイントに少なくとも1つのエラーレスポンスがある
- 共通オブジェクトは
components.schemasに定義されている - 共通スキーマはコピーせず
$refで参照している - 必須フィールドは
requiredに明記されている -
uuid、email、date-timeなどのformatが必要な箇所に設定されている - クエリパラメータのデフォルト値や最大値が定義されている
- スペックファイルがGitにコミットされている
- スペック変更がプルリクエストでレビューされる
- モックサーバーがスペックから起動できる
- フロントエンドがモックAPIへアクセスできる
- 契約テストで実レスポンスをスキーマに照合している
- 公開ドキュメントが同じスペックから生成されている
すべて満たしていれば、チームは別々の推測ではなく、1つの合意に基づいて並行開発できます。
FAQ
スペックファーストAPI開発はデザインファーストと同じですか?
ほとんどの場合、同じ原則を指します。
「デザインファースト」「契約ファースト」「スペックファースト」はいずれも、実装前にインターフェースを定義する考え方です。
その中でも「スペックファースト」は、OpenAPIスペックファイルを最初に扱う成果物として明確に示す言葉です。実務では、これらの用語が近い意味で使われることが多いです。
YAMLを手作業で書く必要がありますか?
必須ではありません。
ビジュアルエディタでAPIを設計し、OpenAPI YAMLを生成しても構いません。逆に、エディタで直接YAMLを書いても問題ありません。
重要なのは、入力方法ではなく、コードを書く前に契約が存在し、チームで合意されていることです。
多くのチームでは、次のように併用します。
- 初期設計はビジュアルエディタで作る
- レビュー時はYAML差分を見る
- 細かい修正はYAMLで直接行う
- 最終的な契約はGitで管理する
スペックとコードが乖離するのをどう防ぎますか?
スペックを信頼できる唯一の情報源として扱い、それをテストとレビューで強制します。
実践することは次の3つです。
- スペックをGitで管理する
- スペック変更をプルリクエストでレビューする
- CIで契約テストを実行する
レスポンスがスキーマと一致しなくなった場合にビルドを失敗させれば、実装と契約のずれを早期に検出できます。
スペックファーストAPI開発は、作業順序を少し変えるだけです。先に契約を書き、合意し、それに基づいて実装します。この流れを試すには、Apidogでスペックファーストモードを開き、自分のリポジトリを指定してください。


Top comments (0)