Skip to main content

MSW (Mock Service Worker)

An API mocking library that intercepts network requests at the Service Worker level in browsers and via a Node.js interception layer in tests. Define request handlers once and use them across your browser dev environment, Jest, and Vitest test suites — no application code changes needed.

✓ Browser + Node.js ✓ REST and GraphQL ✓ Works with any HTTP client ✓ One-time response overrides ✓ No proxy server needed ✓ TypeScript native

What is MSW?

Mock Service Worker is an API mocking library that sits at the network layer rather than patching your HTTP client. In the browser, it registers a Service Worker that intercepts outgoing requests before they leave the browser tab. In Node.js environments (Jest, Vitest, server-side rendering), it uses a lower-level HTTP interception layer with the same API.

The key advantage is that your application code is unaware of the mocking. Whether your app uses fetch, axios, ky, or any other library, MSW handlers intercept the requests transparently. This makes MSW mocks more realistic — the app goes through the same code path as it would with a real API.

Key Features

Installation

# Install MSW
npm install msw --save-dev

# Initialize the Service Worker file for browser usage
npx msw init public/ --save

# The above copies mockServiceWorker.js into your public directory
# Add "workerDirectory": "public" to package.json msw config if needed

Defining REST Handlers

Define handlers using http.get(), http.post(), and other HTTP method helpers. Return HttpResponse.json() for JSON responses or construct a custom HttpResponse for other status codes.

// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  // GET /api/users — return a list of users
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'Alice', email: '[email protected]' },
      { id: 2, name: 'Bob',   email: '[email protected]'   },
    ]);
  }),

  // POST /api/users — create a user and echo it back
  http.post('/api/users', async ({ request }) => {
    const body = await request.json() as { name: string; email: string };
    return HttpResponse.json(
      { id: Date.now(), ...body },
      { status: 201 }
    );
  }),

  // Simulate a 404 for a missing resource
  http.get('/api/users/:id', ({ params }) => {
    if (Number(params.id) > 100) {
      return new HttpResponse(null, { status: 404 });
    }
    return HttpResponse.json({ id: params.id, name: 'Alice' });
  }),
];

Browser Setup

Set up the Service Worker with setupWorker() from msw/browser. Start it conditionally in development so it never activates in production builds.

// src/mocks/browser.ts — browser setup
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';

export const worker = setupWorker(...handlers);

// src/main.tsx (or index.tsx) — start the worker in development
async function enableMocking() {
  if (process.env.NODE_ENV !== 'development') return;

  const { worker } = await import('./mocks/browser');
  // Start the Service Worker; onUnhandledRequest warns about missing handlers
  return worker.start({ onUnhandledRequest: 'warn' });
}

enableMocking().then(() => {
  ReactDOM.createRoot(document.getElementById('root')!).render();
});

Node.js Setup (Tests)

For tests, use setupServer() from msw/node. Start, reset, and stop the server in your test setup file. Call server.resetHandlers() after each test to remove any per-test overrides.

// src/mocks/node.ts — Node.js setup (for tests)
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

// vitest.setup.ts or jest.setup.ts
import { server } from './src/mocks/node';
import { afterAll, afterEach, beforeAll } from 'vitest';

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());  // reset overrides between tests
afterAll(() => server.close());

One-Time Handler Overrides

Use server.use() inside a test to temporarily override a handler for that test only. This is the standard pattern for testing error states, empty responses, and edge cases.

// One-time handler override inside a specific test
import { http, HttpResponse } from 'msw';
import { server } from '../mocks/node';
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';

test('shows an error when the API fails', async () => {
  // Override for this test only — resets after the test via afterEach
  server.use(
    http.get('/api/users', () => {
      return new HttpResponse(null, { status: 500 });
    })
  );

  render();

  await waitFor(() => {
    expect(screen.getByText('Failed to load users')).toBeInTheDocument();
  });
});

GraphQL Handlers

Import graphql from msw and define handlers by operation name. Works for any GraphQL client — Apollo, urql, or raw fetch.

// GraphQL handler with MSW
import { graphql, HttpResponse } from 'msw';

export const graphqlHandlers = [
  graphql.query('GetUser', ({ variables }) => {
    return HttpResponse.json({
      data: {
        user: {
          id: variables.id,
          name: 'Alice',
          email: '[email protected]',
        },
      },
    });
  }),

  graphql.mutation('CreateUser', ({ variables }) => {
    return HttpResponse.json({
      data: {
        createUser: { id: '99', ...variables.input },
      },
    });
  }),
];

Pros and Cons

Pros

  • ✓ Works at network level — agnostic to HTTP client library
  • ✓ Same handlers used in browser dev and Node.js tests
  • ✓ Realistic mock behavior — app runs the same code path
  • ✓ First-class TypeScript support
  • ✓ One-time overrides per test without global state pollution

Cons

  • ✗ Requires serving mockServiceWorker.js from your public directory
  • ✗ Service Worker requires HTTPS or localhost (no arbitrary domains)
  • ✗ v2 API has breaking changes from v1 — migration required for older codebases
  • ✗ Not suitable for mocking WebSocket connections (use a different tool)
  • ✗ Slight overhead in test setup compared to in-module mocking

Frequently Asked Questions

What is Mock Service Worker (MSW)?▼

Mock Service Worker is an API mocking library that intercepts outgoing network requests using a Service Worker (in the browser) or a Node.js interceptor layer (in tests). It lets you define request handlers that return mock responses, without modifying your application code or using a proxy server. This means your app code runs unchanged — MSW intercepts at the network layer.

How does MSW differ from mocking fetch or axios directly?▼

Direct mocking (jest.mock('axios') or vi.spyOn(window, 'fetch')) replaces the underlying HTTP function with a mock, which ties your test to the implementation detail of which HTTP library is used. MSW intercepts at the network level, so it works regardless of whether your app uses fetch, axios, ky, or any other client. Switching your app from fetch to axios does not break any MSW handlers.

Do I need to configure anything different for browser vs Node.js?▼

Yes. For browsers, MSW uses a Service Worker (mockServiceWorker.js) that you serve from your public directory. Initialize it with npx msw init public/ and start it with worker.start() in development. For Node.js (tests, SSR), MSW uses a request interception layer without a Service Worker. Set up a server with setupServer() from msw/node and start it in your test setup file. Both use the same handlers.

How do I handle one-time response overrides in tests?▼

Use server.use() inside a test to add a temporary handler that takes priority over the default handlers. This override is active only for that test — the afterEach(() => server.resetHandlers()) call in your setup file removes it after each test, restoring the default handlers. This pattern is ideal for testing error states, empty states, or edge cases without changing the default mock data.

Does MSW support GraphQL?▼

Yes. Import the graphql object from msw and define handlers with graphql.query() and graphql.mutation(). MSW matches GraphQL requests by operation name (extracted from the request body). Response shape follows the standard GraphQL format with a data key. This works for both REST-over-HTTP GraphQL endpoints and Apollo Client setups.

Can MSW be used in production or only in development and tests?▼

MSW is primarily a development and testing tool. The recommended pattern is to conditionally start the Service Worker only when NODE_ENV is 'development', so it never activates in production builds. In test environments, the Node.js server is started in setup files and torn down after all tests complete. Running MSW in production would intercept real API calls, which is almost never desired.

Share this tool: X / Twitter LinkedIn
✓ 60,000+ developers use DevPlaybook ★★★★★ 4.8 / 5 on Gumroad ✓ MIT licensed · Instant download ✓ No login required

Related Products

on Gumroad

You Might Also Need

More Free Tools

Vitest Configuration Generator GraphQL Query Builder OpenAPI Validator GraphQL Fragment Generator Jest Coverage Config Generator GraphQL Schema Validator v2 GraphQL Query Formatter Jest Testing Framework Guide Vitest Guide — Vite-native Testing Jest to Vitest Migrator API Test Case Generator API Response Mock Generator API Response Time Tester OpenAPI Schema Validator OpenAPI Mock Generator API Request Builder HTTP API Tester API Mock Server Config Generator Mock Service Worker (MSW) API Mocking Guide GraphQL Client Configurator Jest Mock Generator API Rate Limit Calculator API Response Formatter & Analyzer LLM Cost Calculator AI Prompt Cost Calculator REST API Request Builder Cypress Frontend Testing Guide LLM Prompt Cost Calculator Test Coverage Calculator API Response Mocker AI Rate Limit Calculator API Key Generator AI Doc Generator — Pro Tool AI Unit Test Generator — Pro Tool HTTP Methods Reference HTTP Code Snippet Generator HTTP Status Code Reference HTTP Status Code Reference JSON Diff Viewer JSON Diff Rate Limiter Tester API Changelog Generator HTTP Mock Response Builder Rate Limit Calculator CORS Policy Builder HTTP Status Code Lookup HTTP Status Code Lookup Vue Component Generator HTTP Status Code Reference LLM Context Window Calculator LLM Token Counter k6 Load Test Script Generator Playwright E2E Test Generator Prompt Token Estimator Playwright E2E Testing Guide Pulumi Modern IaC Guide HTML5 Canvas API Playground Prompt Injection Tester Token Context Calculator OpenAI Function Calling Generator Token Counter & Cost Estimator AI API Rate Limit Calculator

Enjoying this tool? Get more like it.

Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.

DevPlaybook Pro 7 days free

Unlock AI-Powered Dev Tools

  • ⚡ AI Code Review, Doc Generator & SQL Builder
  • ⚡ All premium templates & early access
  • ⚡ Member discounts on Gumroad products
Start Free Trial →

$9/mo after trial · Cancel anytime

Want the full toolkit?

Get DevPlaybook Pro

Every template, guide, boilerplate, and automation script in one bundle. 13 premium products — grab them all at once and save big.

$79
$241 separately Save $162 (67% off)
Get DevPlaybook Pro — $79 →

MIT licensed · Instant download · No subscription

See what's included → Browse all deals →