DEV Community

Alex Spinov
Alex Spinov

Posted on

MSW Has a Free API Mocking Framework — Stop Using Custom Mock Servers

You're probably mocking APIs wrong. Custom mock servers, intercepting fetch globally, hardcoding responses — all fragile, all pain.

MSW (Mock Service Worker) fixes this at the network level.

What is MSW?

MSW intercepts HTTP requests at the network level using Service Workers (browser) or custom interceptors (Node.js). Your application code doesn't know it's being mocked. That's the point.

Why MSW Changes Everything

1. Network-Level Interception

import { http, HttpResponse } from 'msw';

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

  http.post('/api/users', async ({ request }) => {
    const user = await request.json();
    return HttpResponse.json({ id: 3, ...user }, { status: 201 });
  }),
];
Enter fullscreen mode Exit fullscreen mode

Your fetch(), axios, ky — all intercepted. No code changes needed.

2. Same Mocks for Browser AND Node.js

// Browser (Storybook, dev server)
import { setupWorker } from 'msw/browser';
const worker = setupWorker(...handlers);
worker.start();

// Node.js (tests, SSR)
import { setupServer } from 'msw/node';
const server = setupServer(...handlers);
server.listen();
Enter fullscreen mode Exit fullscreen mode

Write mocks once. Use everywhere.

3. Request Handlers That Feel Natural

import { http, HttpResponse } from 'msw';

const handlers = [
  // Path parameters
  http.get('/api/users/:id', ({ params }) => {
    return HttpResponse.json({ id: params.id, name: 'Alice' });
  }),

  // Query parameters
  http.get('/api/search', ({ request }) => {
    const url = new URL(request.url);
    const query = url.searchParams.get('q');
    return HttpResponse.json({ results: [`Result for ${query}`] });
  }),

  // Error responses
  http.get('/api/protected', () => {
    return HttpResponse.json(
      { error: 'Unauthorized' },
      { status: 401 }
    );
  }),

  // Network errors
  http.get('/api/unstable', () => {
    return HttpResponse.error();
  }),
];
Enter fullscreen mode Exit fullscreen mode

4. Perfect for Testing

import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';

const server = setupServer(...handlers);

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

test('shows user profile', async () => {
  // Override for this specific test
  server.use(
    http.get('/api/users/1', () => {
      return HttpResponse.json({ id: 1, name: 'Test User', role: 'admin' });
    })
  );

  render(<UserProfile userId="1" />);
  expect(await screen.findByText('Test User')).toBeInTheDocument();
});

test('handles API errors gracefully', async () => {
  server.use(
    http.get('/api/users/1', () => {
      return HttpResponse.json({ error: 'Not found' }, { status: 404 });
    })
  );

  render(<UserProfile userId="1" />);
  expect(await screen.findByText('User not found')).toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

5. WebSocket Mocking

import { ws } from 'msw';

const chat = ws.link('wss://api.example.com/chat');

export const handlers = [
  chat.addEventListener('connection', ({ client }) => {
    client.send(JSON.stringify({ type: 'connected', timestamp: Date.now() }));

    client.addEventListener('message', (event) => {
      client.send(JSON.stringify({
        type: 'echo',
        data: event.data
      }));
    });
  }),
];
Enter fullscreen mode Exit fullscreen mode

MSW vs Other Mocking Approaches

MSW Jest mocks Nock JSON Server
Level Network Module HTTP Server
Browser Yes No No Yes
Node.js Yes Yes Yes Yes
Framework agnostic Yes Jest only Node only Yes
WebSocket Yes No No No
No code changes Yes Requires import changes Yes Yes

Getting Started

npm install msw --save-dev
npx msw init public/ --save
Enter fullscreen mode Exit fullscreen mode

Create src/mocks/handlers.ts, define your handlers, and you're done.

The Bottom Line

MSW is the standard for API mocking in 2026. Network-level interception, cross-environment support, and WebSocket mocking make it the tool your test suite has been missing.


Building data-intensive applications? I create custom web scraping and data extraction tools. Check out my Apify actors or email spinov001@gmail.com.

Top comments (0)