MSW (Mock Service Worker) intercepts network requests at the service worker level. Mock APIs for development, testing, and Storybook — without changing your application code.
Why MSW?
- Network-level mocking — intercepts fetch/XHR, not your code
- Same handlers — use the same mocks in browser AND tests
- Storybook integration — mock APIs in stories
- TypeScript — fully typed request/response handlers
Quick Start
npm install msw --save-dev
npx msw init public/ --save # For browser
Handlers
// mocks/handlers.ts
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 body = await request.json();
return HttpResponse.json({ id: 3, ...body }, { status: 201 });
}),
http.get('/api/users/:id', ({ params }) => {
return HttpResponse.json({ id: params.id, name: 'Alice' });
}),
http.delete('/api/users/:id', () => {
return new HttpResponse(null, { status: 204 });
}),
];
Browser Setup
// mocks/browser.ts
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
// main.tsx
if (process.env.NODE_ENV === 'development') {
const { worker } = await import('./mocks/browser');
await worker.start();
}
Testing Setup
// vitest.setup.ts
import { setupServer } from 'msw/node';
import { handlers } from './mocks/handlers';
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
// Override for specific test
test('handles error', async () => {
server.use(
http.get('/api/users', () => {
return HttpResponse.json({ error: 'Failed' }, { status: 500 });
})
);
// Test error handling...
});
Need mock data for development? Check out my Apify actors for real web data, or email spinov001@gmail.com for custom API solutions.
MSW, Mirage JS, or json-server — how do you mock APIs? Share below!
Top comments (0)