Claude Code × API開発自動化完全ガイド|OpenAPI・tRPCスキーマファースト開発・テスト自動生成・CI連携【2026年最新】

Claude Code × API開発自動化完全ガイド|OpenAPI・tRPCスキーマファースト開発・テスト自動生成・CI連携【2026年最新】 AI開発

API開発で最も非効率な作業は「仕様書とコードの二重メンテナンス」です。OpenAPIのYAMLを書いて、それに合わせてルーターを実装して、型定義を手書きして、テストも書く——この繰り返しに時間を取られていませんか?

スキーマファースト開発では、まずスキーマ(仕様)を定義し、そこからコード・型・テストを自動生成します。Claude Codeはこのワークフローを加速する最適なツールです。要件からOpenAPI YAMLを一発生成し、tRPCのルーターをZodスキーマ付きで自動構築し、スキーマ変更のbreaking changeをCIで自動検出する——この記事ではその具体的な方法を解説します。

スポンサーリンク

スキーマファースト開発とは

スキーマファースト開発は、APIの仕様書(スキーマ)を最初に定義し、実装コードはスキーマから自動生成するアプローチです。2026年のTypeScript開発では2つのパターンが主流です。

パターン スキーマ 生成物 ユースケース
OpenAPIファースト OpenAPI 3.1 YAML 型定義・クライアント・バリデーション・モック 公開API・複数言語クライアント・外部連携
tRPC + Zodファースト Zodスキーマ 型推論・ルーター・クライアント(全自動) フルスタックTypeScript・社内モノレポ
どちらのパターンでも、Claude Codeに要件を伝えるだけでスキーマ→実装→テストを一気通貫で生成できます。手書きの二重メンテナンスが不要になります。

CLAUDE.mdへのAPI設計ルール記述

CLAUDE.md(API開発ルール)
## API設計ルール

### 原則
- スキーマファースト: OpenAPI 3.1 YAMLまたはZodスキーマを先に定義し、コードを生成する
- 手書きfetch禁止: openapi-fetchまたはtRPCクライアントを使う
- レスポンス形式: { data: T, error: null } | { data: null, error: { code, message } }
- 認証: Bearer token (Authorization header)
- バージョニング: URLパス /api/v1/
- エラーフォーマット: RFC 7807 Problem Details準拠

### OpenAPIルール
- 仕様ファイル: openapi/openapi.yaml
- 新規エンドポイント追加時は必ずopenapi.yamlを先に更新
- コード生成: openapi-typescript + openapi-fetchを使用
- バリデーション: npx @redocly/cli lint openapi/openapi.yaml

### tRPCルール
- Zodスキーマを先に定義してからルーターを実装
- inputバリデーションは必ずZodスキーマで定義
- Drizzle連携時はdrizzle-zodのcreateInsertSchema/createSelectSchemaを使う
- SuperjsonはhttpBatchLink内のtransformerに指定(v11記法)

### テスト
- OpenAPI: Orval + MSWでモック自動生成
- tRPC: supertest + callerによる直接テスト
- テストカバレッジ: エンドポイント100%

CLAUDE.mdの詳しい書き方はCLAUDE.md完全ガイドで解説しています。

OpenAPIスキーマファースト開発

要件からOpenAPI YAMLを自動生成する

OpenAPI YAML生成プロンプト
以下の要件からOpenAPI 3.1仕様のYAMLを生成してください:

エンドポイント:
- GET /api/v1/users(一覧取得、cursorページネーション)
- POST /api/v1/users(新規作成)
- GET /api/v1/users/{id}(詳細取得)
- PUT /api/v1/users/{id}(更新)
- DELETE /api/v1/users/{id}(削除)

共通仕様:
- 認証: Bearer token
- エラー: RFC 7807 Problem Details
- レスポンス: { data: T } / { error: { type, title, status, detail } }
- cursorページネーション: ?cursor=xxx&limit=20

出力先: openapi/openapi.yaml
スキーマのcomponents/schemasも含めてください。
生成されるOpenAPI YAML(抜粋)
openapi: "3.1.0"
info:
  title: User API
  version: "1.0.0"
servers:
  - url: /api/v1

paths:
  /users:
    get:
      operationId: listUsers
      summary: ユーザー一覧取得
      parameters:
        - name: cursor
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, default: 20, maximum: 100 }
      responses:
        "200":
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items: { $ref: "#/components/schemas/User" }
                  nextCursor:
                    type: string
                    nullable: true
      security:
        - BearerAuth: []

components:
  schemas:
    User:
      type: object
      required: [id, name, email, createdAt]
      properties:
        id: { type: string, format: uuid }
        name: { type: string, minLength: 1, maxLength: 100 }
        email: { type: string, format: email }
        createdAt: { type: string, format: date-time }
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

OpenAPIから型定義・クライアントを自動生成する

生成したYAMLから、TypeScriptの型定義と型安全なHTTPクライアントを自動生成します。

コード生成コマンド
# openapi-typescriptで型定義を生成
npx openapi-typescript openapi/openapi.yaml -o src/api/schema.d.ts

# 生成された型を使ってopenapi-fetchでAPIクライアントを構築
npm install openapi-fetch
型安全なAPIクライアント(openapi-fetch)
import createClient from "openapi-fetch";
import type { paths } from "./schema";

const api = createClient<paths>({ baseUrl: "/api/v1" });

// 完全に型安全(パス・パラメータ・レスポンスすべて型チェック)
const { data, error } = await api.GET("/users/{id}", {
  params: { path: { id: "user-123" } },
});
// data は User | undefined として推論される

// 一覧取得(cursorページネーション)
const { data: users } = await api.GET("/users", {
  params: { query: { cursor: "abc", limit: 20 } },
});
openapi-fetchはバンドルサイズ6KB・300K ops/sで、他のOpenAPIクライアント生成ツールの中で最も軽量かつ高速です。TanStack QueryやSWRとの統合も簡単にできます。

tRPC v11 + Zodスキーマファースト開発

フルスタックTypeScriptプロジェクト(Next.js + TypeScriptバックエンド)では、tRPCを使うと型定義の手動管理が完全に不要になります。

Zod → tRPCルーター → クライアントの一気通貫生成

tRPCルーター生成プロンプト
tRPC v11でユーザーCRUDルーターを実装してください。

要件:
- Zodスキーマで入力バリデーション
- Drizzle ORMでDB操作(drizzle-zodのcreateInsertSchemaを使用)
- cursorページネーション
- エラーハンドリング(TRPCError)
- v11記法(createTRPCClient、transformerはlink内)

テーブル定義(db/schema.ts)はすでにあります。
生成されるtRPCルーター
import { z } from "zod";
import { router, publicProcedure } from "../trpc";
import { users } from "@/db/schema";
import { createInsertSchema } from "drizzle-orm/zod";
import { eq, gt, asc } from "drizzle-orm";
import { TRPCError } from "@trpc/server";

// DBスキーマからZodスキーマを自動生成
const createUserInput = createInsertSchema(users).omit({ id: true, createdAt: true });
const updateUserInput = createInsertSchema(users).partial().omit({ id: true, createdAt: true });

export const userRouter = router({
  list: publicProcedure
    .input(z.object({
      cursor: z.string().optional(),
      limit: z.number().min(1).max(100).default(20),
    }))
    .query(async ({ input, ctx }) => {
      const items = await ctx.db
        .select()
        .from(users)
        .where(input.cursor ? gt(users.id, Number(input.cursor)) : undefined)
        .orderBy(asc(users.id))
        .limit(input.limit + 1);

      const hasMore = items.length > input.limit;
      const data = hasMore ? items.slice(0, -1) : items;

      return {
        data,
        nextCursor: hasMore ? String(data.at(-1)?.id) : null,
      };
    }),

  getById: publicProcedure
    .input(z.object({ id: z.number() }))
    .query(async ({ input, ctx }) => {
      const [user] = await ctx.db
        .select()
        .from(users)
        .where(eq(users.id, input.id));
      if (!user) throw new TRPCError({ code: "NOT_FOUND", message: "ユーザーが見つかりません" });
      return user;
    }),

  create: publicProcedure
    .input(createUserInput)
    .mutation(async ({ input, ctx }) => {
      const [user] = await ctx.db.insert(users).values(input).returning();
      return user;
    }),

  update: publicProcedure
    .input(z.object({ id: z.number(), data: updateUserInput }))
    .mutation(async ({ input, ctx }) => {
      const [user] = await ctx.db
        .update(users)
        .set(input.data)
        .where(eq(users.id, input.id))
        .returning();
      if (!user) throw new TRPCError({ code: "NOT_FOUND" });
      return user;
    }),

  delete: publicProcedure
    .input(z.object({ id: z.number() }))
    .mutation(async ({ input, ctx }) => {
      await ctx.db.delete(users).where(eq(users.id, input.id));
      return { success: true };
    }),
});
クライアント側(完全型推論、v11記法)
import { createTRPCClient, httpBatchLink } from "@trpc/client";
import superjson from "superjson";
import type { AppRouter } from "@/server/router";

const trpc = createTRPCClient<AppRouter>({
  links: [
    httpBatchLink({
      url: "/api/trpc",
      transformer: superjson,  // v11: transformerはlink内に指定
    }),
  ],
});

// 完全に型安全(引数・戻り値すべて型推論される)
const user = await trpc.user.create.mutate({
  name: "田中太郎",
  email: "tanaka@example.com",
});
// user は { id: number, name: string, email: string, ... } として推論
tRPC v11ではcreateTRPCProxyClientcreateTRPCClientに変更され、.interop()モード(v9互換)は完全廃止されました。またtransformerの指定場所がクライアント初期化からlink内に移動しています。CLAUDE.mdに明記しておきましょう。

tRPCの基本的な使い方はTypeScript × tRPC完全ガイドをご覧ください。

REST(OpenAPI)vs tRPC ── 使い分けの判断基準

判断基準 REST + OpenAPI tRPC
クライアントの言語 複数言語(Swift/Kotlin/Python等) TypeScriptのみ
APIの公開範囲 外部公開・サードパーティ連携 社内・モノレポ内
リポジトリ構成 サーバー・クライアント分離 モノレポまたは単一リポジトリ
型安全性 コード生成で担保 型推論で自動担保
ドキュメント OpenAPI仕様書が自動生成 なし(TypeScript型が仕様書代わり)
ランタイムバリデーション 別途実装が必要 Zodで自動
2026年の典型的な構成: 公開APIはREST + OpenAPIで外部連携、フロントエンド→BFFはtRPCでモノレポ内通信。この「二刀流」がスタンダードです。

スキーマからテスト・モックを自動生成する

OpenAPI → MSWモック自動生成(Orval)

Orvalを使うと、OpenAPI仕様書からMSW(Mock Service Worker)のハンドラーとFaker.jsの生成データを一括で自動生成できます。バックエンドが未完成でもフロントエンドのテストが書けます。

orval.config.yaml
petstore:
  input: ./openapi/openapi.yaml
  output:
    target: ./src/api/generated
    client: react-query    # TanStack Queryフック自動生成
    mock: true             # MSWハンドラー自動生成
Orvalの実行
# コード生成(型・クライアント・MSWモック一括)
npx orval

# 生成されるファイル:
# src/api/generated/
#   ├── users.ts           # TanStack Queryフック
#   ├── users.msw.ts       # MSWハンドラー
#   └── model/             # 型定義
生成されたMSWモックを使ったテスト
import { setupServer } from "msw/node";
import { getUsersHandler, getUsersByIdHandler } from "@/api/generated/users.msw";
import { render, screen } from "@testing-library/react";
import { UserList } from "./UserList";

// OpenAPIから自動生成されたMSWハンドラーを使用
const server = setupServer(...getUsersHandler(), ...getUsersByIdHandler());

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

test("ユーザー一覧が表示される", async () => {
  render(<UserList />);
  // MSWがOpenAPIスキーマに基づくモックデータを返す
  expect(await screen.findByText(/ユーザー/)).toBeTruthy();
});

tRPC → callerによるダイレクトテスト

tRPCルーターの直接テスト
import { appRouter } from "@/server/router";
import { createCallerFactory } from "@trpc/server";

const createCaller = createCallerFactory(appRouter);

describe("userRouter", () => {
  it("ユーザーを作成して取得できる", async () => {
    const caller = createCaller({ db: testDb });

    // 作成
    const created = await caller.user.create({
      name: "テストユーザー",
      email: "test@example.com",
    });
    expect(created.id).toBeDefined();

    // 取得
    const user = await caller.user.getById({ id: created.id });
    expect(user.name).toBe("テストユーザー");
  });

  it("存在しないユーザーでNOT_FOUNDエラー", async () => {
    const caller = createCaller({ db: testDb });
    await expect(
      caller.user.getById({ id: 999999 })
    ).rejects.toThrow("NOT_FOUND");
  });
});

テスト全般の戦略はClaude Codeテスト完全ガイドで解説しています。

CIでAPIスキーマのbreaking changeを自動検出する

OpenAPI仕様書が変更されたとき、後方互換性を壊す変更(breaking change)をCIで自動検出できます。

.github/workflows/api-schema-check.yml
name: API Schema Check
on:
  pull_request:
    paths:
      - "openapi/**"

jobs:
  breaking-changes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # OpenAPIバリデーション
      - name: Lint OpenAPI
        run: npx @redocly/cli lint openapi/openapi.yaml

      # Breaking Change検出(oasdiff)
      - uses: oasdiff/oasdiff-action/breaking@v0.0.37
        with:
          base: "main:openapi/openapi.yaml"    # mainブランチの仕様
          revision: "openapi/openapi.yaml"      # PRの仕様
          fail-on: ERR  # breaking changeでCI失敗

oasdiffは300以上のbreaking changeルールを持ち、以下のような変更を自動検出します:

  • 必須フィールドの追加
  • フィールドの型変更(string→number等)
  • エンドポイントの削除
  • レスポンスコードの削除
  • 認証要件の変更

GitHub Actionsの設定方法はClaude Code GitHub Actions完全ガイドをご覧ください。

よくある質問

QOpenAPIファーストとtRPCファーストのどちらを選ぶべきですか?
A外部クライアントやモバイルアプリ(非TypeScript)にAPIを公開する場合はOpenAPIファーストが適しています。フルスタックTypeScript(Next.js等)のモノレポ内通信にはtRPCが最適です。両方を併用するケースも多く、公開APIはREST + OpenAPI、内部BFFはtRPCという構成が2026年のスタンダードです。
QtRPCからOpenAPI仕様書を自動生成できますか?
Aはい。tRPC v11公式の@trpc/openapi(alpha)で、tRPCルーターからOpenAPI仕様書をCLI生成できます。またoRPC(tRPC互換フレームワーク)はOpenAPI生成を組み込みでサポートしており、新規プロジェクトでの有力な選択肢です。
QClaude Codeが古いtRPC記法(v10)でコードを生成します。どう防ぎますか?
ACLAUDE.mdに以下を明記してください:「tRPC v11記法を使用。createTRPCProxyClientではなくcreateTRPCClienttransformerはlink内に指定。.interop()モードは使用しない。TypeScript 5.7.2+・Node.js 18+必須」
QOpenAPIのバリデーションはどのタイミングで行うべきですか?
ACIの早い段階で行うべきです。PRを作成するたびに@redocly/cli lintでバリデーション、oasdiffでbreaking change検出を実行するのが推奨パターンです。Claude Codeにスキーマ変更を依頼した直後にnpx @redocly/cli lint openapi/openapi.yamlを実行させる習慣もつけましょう。
Qdrizzle-zodのcreateInsertSchemaとtRPCのinputは型が一致しますか?
Aはい、drizzle-zodcreateInsertSchemaはZodスキーマを返すため、tRPCの.input()にそのまま渡せます。DB定義→Zodスキーマ→tRPC inputバリデーションの一気通貫が実現します。.omit().partial()でcreate/update用にカスタマイズもできます。

まとめ

スキーマファースト開発をClaude Codeで実践するためのポイントをまとめます。

  • OpenAPIファースト: 要件→OpenAPI YAML→openapi-typescript+openapi-fetchで型安全クライアント自動生成
  • tRPC + Zodファースト: Zodスキーマ→tRPC v11ルーター→クライアント型推論で手動型定義ゼロ
  • DBスキーマ逆生成: drizzle-zodでDBスキーマ→Zodスキーマ→tRPC inputバリデーションを一気通貫
  • テスト自動生成: Orval + MSWでOpenAPIからモック・テストヘルパーを自動生成
  • breaking change検出: oasdiff + GitHub ActionsでPRごとにスキーマ互換性を自動チェック
  • 使い分け: 公開API→REST + OpenAPI、内部BFF→tRPC。「二刀流」が2026年のスタンダード

データベース連携はClaude Code × データベース開発ガイド、Next.jsとの統合はClaude Code × Next.js完全ガイドもあわせてご覧ください。