MSW API with Claude Code
Learn about msw api using Claude Code. Practical tips and code examples included.
MSWでリアルなAPIモック環境を構築する
MSW(Mock Service Worker)はService Workerを利用してネットワークレベルでAPIリクエストをインターセプトするモックライブラリです。テストとブラウザ開発の両方で同じモックハンドラーを共有でき、実際のHTTPリクエストと同じ振る舞いを再現します。Claude Codeはハンドラーの設計から型安全なモック構築まで的確にサポートします。
基本的なハンドラー設計
Claude Codeにハンドラーの構成を依頼しましょう。
> MSWでユーザーAPIのモックハンドラーを設計して。
> CRUD操作、エラーハンドリング、レスポンス遅延を含めて。
import { http, HttpResponse, delay } from "msw";
// インメモリデータベース
let users: User[] = [
{ id: "1", name: "Alice", email: "[email protected]", role: "admin" },
{ id: "2", name: "Bob", email: "[email protected]", role: "editor" },
];
export const userHandlers = [
// 一覧取得
http.get("/api/users", async ({ request }) => {
await delay(100);
const url = new URL(request.url);
const page = Number(url.searchParams.get("page") || "1");
const perPage = Number(url.searchParams.get("perPage") || "20");
const search = url.searchParams.get("search") || "";
let filtered = users;
if (search) {
filtered = users.filter(
(u) =>
u.name.toLowerCase().includes(search.toLowerCase()) ||
u.email.toLowerCase().includes(search.toLowerCase())
);
}
const start = (page - 1) * perPage;
const paginated = filtered.slice(start, start + perPage);
return HttpResponse.json({
data: paginated,
meta: { total: filtered.length, page, perPage },
});
}),
// 詳細取得
http.get("/api/users/:id", async ({ params }) => {
await delay(50);
const user = users.find((u) => u.id === params.id);
if (!user) {
return HttpResponse.json(
{ error: "ユーザーが見つかりません" },
{ status: 404 }
);
}
return HttpResponse.json({ data: user });
}),
// 作成
http.post("/api/users", async ({ request }) => {
await delay(200);
const body = (await request.json()) as Omit<User, "id">;
const newUser: User = { ...body, id: String(Date.now()) };
users.push(newUser);
return HttpResponse.json({ data: newUser }, { status: 201 });
}),
// 更新
http.put("/api/users/:id", async ({ params, request }) => {
await delay(150);
const body = (await request.json()) as Partial<User>;
const index = users.findIndex((u) => u.id === params.id);
if (index === -1) {
return HttpResponse.json(
{ error: "ユーザーが見つかりません" },
{ status: 404 }
);
}
users[index] = { ...users[index], ...body };
return HttpResponse.json({ data: users[index] });
}),
// 削除
http.delete("/api/users/:id", async ({ params }) => {
await delay(100);
users = users.filter((u) => u.id !== params.id);
return new HttpResponse(null, { status: 204 });
}),
];
テストでの統合
Vitestとの統合パターンです。
import { setupServer } from "msw/node";
import { userHandlers } from "./handlers/user";
// テスト用サーバー
const server = setupServer(...userHandlers);
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe("UserList コンポーネント", () => {
it("ユーザー一覧を表示する", async () => {
render(<UserList />);
await waitFor(() => {
expect(screen.getByText("Alice")).toBeInTheDocument();
expect(screen.getByText("Bob")).toBeInTheDocument();
});
});
it("APIエラー時にエラーメッセージを表示する", async () => {
// テスト単位でハンドラーを上書き
server.use(
http.get("/api/users", () => {
return HttpResponse.json(
{ error: "サーバーエラー" },
{ status: 500 }
);
})
);
render(<UserList />);
await waitFor(() => {
expect(screen.getByText("データの取得に失敗しました")).toBeInTheDocument();
});
});
it("ネットワークエラー時にリトライボタンが表示される", async () => {
server.use(
http.get("/api/users", () => {
return HttpResponse.error();
})
);
render(<UserList />);
await waitFor(() => {
expect(screen.getByRole("button", { name: "再試行" })).toBeInTheDocument();
});
});
});
ブラウザでの開発用モック
ブラウザ環境でService Workerとして動作させるパターンです。
// src/mocks/browser.ts
import { setupWorker } from "msw/browser";
import { userHandlers } from "./handlers/user";
import { authHandlers } from "./handlers/auth";
export const worker = setupWorker(...userHandlers, ...authHandlers);
// src/main.tsx
async function enableMocking() {
if (import.meta.env.DEV) {
const { worker } = await import("./mocks/browser");
return worker.start({
onUnhandledRequest: "bypass",
});
}
}
enableMocking().then(() => {
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
});
型安全なハンドラー設計
TypeScriptの型とMSWハンドラーを統合するパターンです。
import { http, HttpResponse } from "msw";
// API型定義
interface ApiEndpoints {
"GET /api/users": {
response: { data: User[]; meta: PaginationMeta };
params: never;
};
"GET /api/users/:id": {
response: { data: User };
params: { id: string };
};
"POST /api/users": {
response: { data: User };
body: CreateUserInput;
params: never;
};
}
// ファクトリー関数
function createUserFactory(overrides: Partial<User> = {}): User {
return {
id: String(Math.random()).slice(2, 10),
name: `User ${Math.random().toString(36).slice(2, 6)}`,
email: `user-${Date.now()}@example.com`,
role: "viewer",
...overrides,
};
}
// テストシナリオ用のハンドラーセット
export const emptyStateHandlers = [
http.get("/api/users", () =>
HttpResponse.json({ data: [], meta: { total: 0, page: 1, perPage: 20 } })
),
];
export const errorHandlers = [
http.get("/api/users", () =>
HttpResponse.json({ error: "Internal Server Error" }, { status: 500 })
),
];
export const slowResponseHandlers = [
http.get("/api/users", async () => {
await delay(5000);
return HttpResponse.json({ data: [createUserFactory()], meta: { total: 1, page: 1, perPage: 20 } });
}),
];
Zusammenfassung
MSWはネットワークレベルのモックにより、実際のAPI通信に近い環境でテストと開発ができます。Claude Codeを活用すれば、ハンドラー設計、テスト統合、型安全な構成を短時間で構築可能です。
ユニットテストとの連携はVitest上級テクニックを、E2Eテストでの活用はPlaywright E2Eテスト実践ガイドを参照してください。MSW公式ドキュメントも確認しておきましょう。
Related Posts
10 Tipps, um Ihre Produktivität mit Claude Code zu verdreifachen
Entdecken Sie 10 praktische Tipps, um mehr aus Claude Code herauszuholen. Von Prompt-Strategien bis zu Workflow-Abkürzungen — diese Techniken steigern Ihre Effizienz ab sofort.
Canvas/WebGL-Optimierung mit Claude Code
Erfahren Sie, wie Sie Canvas/WebGL mit Claude Code optimieren. Mit praktischen Tipps und Codebeispielen.
Markdown Implementation with Claude Code
Learn about markdown implementation using Claude Code. Practical tips and code examples included.