Skip to content
matthewcm.dev

    ↑ ↓ move · ↵ open · tab scopeopen graph →

    MSW (Mock Service Worker)

    Intercept HTTP at the network layer so tests, Storybook and local development all hit realistic mocked APIs while your real fetching and parsing code runs.

    Evergreentended planted 1 min read
    On this page

    MSW intercepts HTTP requests at the network layer — with a service worker in the browser and request interceptors in Node. Your application code calls fetch exactly as it would in production; MSW answers. That one design decision is why it’s become my default for API mocking. The argument for mocking at the network rather than in your code is in Stop Mocking Fetch.

    Minimal setup

    // mocks/handlers.ts
    import { http, HttpResponse } from "msw";
    export const handlers = [
      http.get("/api/workouts/:id", ({ params }) =>
        HttpResponse.json({ id: params.id, name: "Push day" }),
      ),
    ];
    
    // test setup (Node)
    import { setupServer } from "msw/node";
    export const server = setupServer(...handlers);
    beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
    afterEach(() => server.resetHandlers());
    afterAll(() => server.close());
    
    // one test overrides a handler to exercise the error path
    server.use(http.get("/api/workouts/:id", () => new HttpResponse(null, { status: 500 })));

    Habits that keep it honest

    • Strict by default. onUnhandledRequest: "error" fails a test that makes a request you forgot to mock. For tests that expect errors, silence console.error locally rather than loosening the global setting.
    • Shared handlers. One set of handlers serves unit tests, integration tests, Storybook and local development.
    • Override per test with server.use() and reset after each test, so tests stay isolated.
    • Fake data factories (e.g. @mswjs/data) give you a small in-memory database for realistic flows.

    Contract-first development

    The underrated use: run MSW in the browser and you have an offline mode. Agree the API contract with the backend team, write handlers from it, and build the UI before the endpoint exists. When the real API arrives, you switch the mock off.

    The risk is drift — mocks can lie. Generate handlers from an OpenAPI spec where you can, keep a few contract or end-to-end tests against the real backend, and treat a mismatch as a bug in the contract, not the test.

    In practice

    I’ve used MSW to drive a coverage push across several services, to power Storybook for a component library, and to build screens against endpoints that didn’t exist yet. The offline mode turned out to be as valuable as the tests: demos and development stopped depending on a shared test environment being up. Pairs naturally with React Testing Strategy and Playwright.

    • #msw
    • #testing
    • #mocking
    • #api-contracts
    • #developer-experience

    ↪ Links to (3)