LESSON 25分

ストーリー

高橋アーキテクト
API開発には2つのアプローチがある

高橋アーキテクトが2つのフローチャートを描いた。

高橋アーキテクト
1つは”コードファースト”。コードを書いてからAPI仕様書を生成する。もう1つは”仕様ファースト”。仕様書を先に書いてから実装する
あなた
どちらがいいんですか?
高橋アーキテクト
チームで開発するなら、圧倒的に仕様ファーストだ。フロントエンドとバックエンドが並行して開発できるし、仕様の合意が取れてから実装に入るから、手戻りが少ない

コードファースト 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分