ストーリー
高橋アーキテクトが2つのフローチャートを描いた。
コードファースト vs 仕様ファースト
コードファースト
1. バックエンドが実装する
2. 実装からOpenAPI仕様書を自動生成
3. フロントエンドが仕様書を見て実装
4. 「仕様が違う」「この項目が足りない」→ 手戻り
仕様ファースト(API Design First)
1. API仕様書(OpenAPI)を作成する
2. フロントエンド・バックエンドでレビュー
3. 合意したら、並行して開発開始
- フロントエンド: モックサーバーを使って開発
- バックエンド: 仕様に従って実装
4. 結合テスト
比較
| 観点 | コードファースト | 仕様ファースト |
|---|---|---|
| 開発開始 | バックエンドが先行 | 並行して開発可能 |
| 仕様変更のコスト | 実装後の変更は高コスト | 仕様段階なら低コスト |
| フロントエンドの待ち時間 | バックエンド完成まで待つ | モックで即座に開発開始 |
| ドキュメントの鮮度 | 実装と乖離しやすい | 仕様が常にソースオブトゥルース |
| 適する場面 | 個人開発、小規模 | チーム開発、大規模 |
仕様ファースト開発の実践フロー
Phase 1: 設計とレビュー
// 1. API設計者がOpenAPI仕様書を作成
// openapi.yaml を作成
// 2. PRでレビュー
// フロントエンド、バックエンド、QAがレビューに参加
// - エンドポイントは適切か?
// - リクエスト/レスポンスの形式は使いやすいか?
// - エラーケースは網羅されているか?
// - 命名規則は一貫しているか?
// 3. 合意 → マージ
Phase 2: モックサーバーで並行開発
// Prism でモックサーバーを起動
// npx @stoplight/prism-cli mock openapi.yaml --port 4010
// OpenAPI仕様書のexampleに基づいてレスポンスを返す
// GET http://localhost:4010/api/v1/users/123
// → 仕様書のexampleデータがそのまま返る
// フロントエンドはモックサーバーに対して開発を進められる
const API_BASE = process.env.NODE_ENV === 'development'
? 'http://localhost:4010'
: 'https://api.taskflow.example.com';
async function fetchUser(id: string) {
const response = await fetch(`${API_BASE}/api/v1/users/${id}`);
return response.json();
}
Phase 3: 実装とバリデーション
// バックエンドは仕様に従って実装
// express-openapi-validator で仕様との整合性を自動チェック
import * as OpenApiValidator from 'express-openapi-validator';
app.use(
OpenApiValidator.middleware({
apiSpec: './openapi.yaml',
validateRequests: true,
validateResponses: process.env.NODE_ENV !== 'production',
})
);
// 仕様と異なるレスポンスを返すとバリデーションエラーになる
// → 仕様と実装の乖離を自動的に検出
Phase 4: コントラクトテスト
// OpenAPI仕様書を使ったコントラクトテスト
// 実際のAPIレスポンスが仕様に準拠しているか検証
import { describe, it, expect } from 'vitest';
describe('Users API Contract', () => {
it('GET /users/:id のレスポンスが仕様に準拠する', async () => {
const response = await fetch('/api/v1/users/usr_123');
const body = await response.json();
// ステータスコードの確認
expect(response.status).toBe(200);
// レスポンス構造の確認
expect(body).toHaveProperty('data');
expect(body.data).toHaveProperty('id');
expect(body.data).toHaveProperty('name');
expect(body.data).toHaveProperty('email');
expect(body.data).toHaveProperty('createdAt');
// 型の確認
expect(typeof body.data.id).toBe('string');
expect(typeof body.data.name).toBe('string');
});
});
仕様ファーストのベストプラクティス
仕様書の管理
// 1. 仕様書をGitで管理する
// openapi/
// ├── openapi.yaml # メインファイル
// ├── paths/ # パス定義を分割
// │ ├── users.yaml
// │ ├── tasks.yaml
// │ └── orders.yaml
// └── components/ # コンポーネントを分割
// ├── schemas/
// │ ├── User.yaml
// │ └── Task.yaml
// └── responses/
// ├── errors.yaml
// └── pagination.yaml
// 2. CIで仕様書をリントする
// .github/workflows/api-lint.yml
// - npx @redocly/cli lint openapi.yaml
// - npx @redocly/cli bundle openapi.yaml -o dist/openapi.yaml
// 3. 仕様変更は必ずPRで行う
// → フロントエンド・バックエンドのレビューを必須にする
まとめ
| ポイント | 内容 |
|---|---|
| 仕様ファースト | 仕様を先に書き、レビュー後に実装を開始する |
| モックサーバー | Prism 等で仕様書からモックを自動生成 |
| 並行開発 | フロントエンドとバックエンドが同時に開発できる |
| コントラクトテスト | 実装が仕様に準拠しているかを自動検証 |
| 仕様書管理 | Gitで管理、CIでリント、PRでレビュー |
チェックリスト
- コードファーストと仕様ファーストの違いを説明できる
- 仕様ファースト開発の4つのフェーズを理解した
- モックサーバーの活用方法を把握した
- コントラクトテストの概念を理解した
次のステップへ
API仕様ファーストの開発フローを学びました。
次は演習です。実際にOpenAPI仕様書を書いてみましょう。
推定読了時間: 25分