Use Cases

Claude CodeでAPI開発を高速化する:設計からテストまで一気通貫

Claude Codeを使ったREST API開発の高速化テクニックを解説。エンドポイント設計、バリデーション、テスト作成まで具体的なコード例で紹介します。

API開発のボトルネックを解消する

API開発では、エンドポイントの設計、バリデーション、エラーハンドリング、テスト作成と、繰り返し作業が多く発生します。Claude Codeはこれらをまとめて高速に処理できます。

エンドポイントの一括生成

CRUDエンドポイントを一度に作成させましょう。

> 以下のリソースに対するRESTful APIを作成して。
> フレームワークはExpress + TypeScript。
>
> リソース: Task
> フィールド: id, title, description, status, dueDate, createdAt, updatedAt
> エンドポイント: GET /tasks, GET /tasks/:id, POST /tasks, PUT /tasks/:id, DELETE /tasks/:id
> バリデーション: zodを使用
> レスポンス形式: { data, meta, error }

生成されるコードの例

import { Router, Request, Response } from "express";
import { z } from "zod";

const TaskSchema = z.object({
  title: z.string().min(1).max(200),
  description: z.string().optional(),
  status: z.enum(["todo", "in_progress", "done"]).default("todo"),
  dueDate: z.string().datetime().optional(),
});

const router = Router();

router.get("/tasks", async (req: Request, res: Response) => {
  const page = Number(req.query.page) || 1;
  const limit = Number(req.query.limit) || 20;
  const offset = (page - 1) * limit;

  const tasks = await taskRepository.findAll({ offset, limit });
  const total = await taskRepository.count();

  res.json({
    data: tasks,
    meta: { page, limit, total, totalPages: Math.ceil(total / limit) },
  });
});

router.post("/tasks", async (req: Request, res: Response) => {
  const parsed = TaskSchema.safeParse(req.body);
  if (!parsed.success) {
    return res.status(400).json({
      error: { message: "バリデーションエラー", details: parsed.error.issues },
    });
  }

  const task = await taskRepository.create(parsed.data);
  res.status(201).json({ data: task });
});

export default router;

OpenAPI仕様書からの生成

既存のAPI仕様書がある場合、それを渡して実装を生成できます。

> openapi.yaml の定義に基づいて、すべてのエンドポイントを実装して。
> バリデーションはスキーマ定義と一致させて。
> 各エンドポイントのテストも作成して。

APIテストの自動生成

実装とテストをセットで作ると、品質が担保されます。

> POST /tasks エンドポイントのテストを作成して。
> 正常系・異常系・境界値をカバーして。
import request from "supertest";
import { app } from "../app";

describe("POST /tasks", () => {
  it("正常なデータで201を返す", async () => {
    const res = await request(app)
      .post("/tasks")
      .send({ title: "テストタスク", status: "todo" });

    expect(res.status).toBe(201);
    expect(res.body.data.title).toBe("テストタスク");
    expect(res.body.data.id).toBeDefined();
  });

  it("titleが空で400を返す", async () => {
    const res = await request(app)
      .post("/tasks")
      .send({ title: "", status: "todo" });

    expect(res.status).toBe(400);
    expect(res.body.error).toBeDefined();
  });

  it("不正なstatusで400を返す", async () => {
    const res = await request(app)
      .post("/tasks")
      .send({ title: "タスク", status: "invalid" });

    expect(res.status).toBe(400);
  });
});

テスト設計の詳しい戦略はテスト戦略完全ガイドで解説しています。

ミドルウェアの生成

認証、レートリミット、ログなど、共通ミドルウェアも生成できます。

> JWT認証ミドルウェアを作成して。
> - Authorizationヘッダーからトークンを取得
> - 有効期限チェック
> - デコードしたユーザー情報をreq.userにセット
> - エラー時は適切なHTTPステータスを返す
import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";

interface AuthRequest extends Request {
  user?: { id: string; email: string; role: string };
}

export function authenticate(
  req: AuthRequest,
  res: Response,
  next: NextFunction
) {
  const header = req.headers.authorization;

  if (!header?.startsWith("Bearer ")) {
    return res.status(401).json({ error: { message: "認証が必要です" } });
  }

  try {
    const token = header.slice(7);
    const decoded = jwt.verify(token, process.env.JWT_SECRET!) as AuthRequest["user"];
    req.user = decoded;
    next();
  } catch (err) {
    if (err instanceof jwt.TokenExpiredError) {
      return res.status(401).json({ error: { message: "トークンの有効期限切れ" } });
    }
    return res.status(403).json({ error: { message: "無効なトークン" } });
  }
}

エラーハンドリングの統一

API全体で一貫したエラーレスポンスを返す設計についてはエラーハンドリング設計パターンで詳しく解説しています。

DB連携も同時に

APIのリポジトリ層やマイグレーションも同時に作れます。データベース操作の自動化についてはDBマイグレーション自動化を参照してください。

まとめ

Claude CodeでAPI開発を行う際のポイントは、仕様を明確に定義して一括で指示することです。CRUDの繰り返し作業をClaude Codeに任せ、ビジネスロジックの設計に集中しましょう。

APIのベストプラクティスについてはAnthropic公式ドキュメントExpress公式ガイドも参考にしてください。

#Claude Code #API開発 #REST API #Express #バックエンド