MSW Handlers Guide

REST Handlers

// src/mocks/handlers.ts import { http, HttpResponse } from 'msw' export const handlers = [ // GET — return JSON http.get('/api/users', () => { return HttpResponse.json([ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' }, ]) }), // GET with URL params http.get('/api/users/:id', ({ params }) => { const { id } = params if (id === '999') { return new HttpResponse(null, { status: 404 }) } return HttpResponse.json({ id: Number(id), name: 'Alice' }) }), // POST — read request body http.post('/api/users', async ({ request }) => { const body = await request.json() return HttpResponse.json( { id: 3, ...body }, { status: 201 } ) }), // DELETE http.delete('/api/users/:id', () => { return new HttpResponse(null, { status: 204 }) }), ]

GraphQL Handlers

import { graphql, HttpResponse } from 'msw' export const handlers = [ graphql.query('GetUser', ({ variables }) => { const { id } = variables return HttpResponse.json({ data: { user: { id, name: 'Alice', email: 'alice@example.com' }, }, }) }), graphql.mutation('CreateUser', async ({ variables }) => { const { name, email } = variables return HttpResponse.json({ data: { createUser: { id: 99, name, email }, }, }) }), // Error response graphql.query('GetProtected', () => { return HttpResponse.json({ errors: [{ message: 'Unauthorized', extensions: { code: 'UNAUTHORIZED' } }], }) }), ]

Browser Setup

// Initialize service worker // npx msw init public/ --save // src/mocks/browser.ts import { setupWorker } from 'msw/browser' import { handlers } from './handlers' export const worker = setupWorker(...handlers) // src/main.tsx async function enableMocking() { if (process.env.NODE_ENV !== 'development') return const { worker } = await import('./mocks/browser') return worker.start({ onUnhandledRequest: 'warn', // warn on unhandled requests }) } enableMocking().then(() => { ReactDOM.createRoot(document.getElementById('root')!).render(<App />) })

Node.js Setup — Jest Integration

// src/mocks/server.ts import { setupServer } from 'msw/node' import { handlers } from './handlers' export const server = setupServer(...handlers) // jest.setup.ts import { server } from './src/mocks/server' import '@testing-library/jest-dom' beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) afterEach(() => server.resetHandlers()) afterAll(() => server.close()) // In a test — override handler for one test import { http, HttpResponse } from 'msw' import { server } from '../mocks/server' import { render, screen, waitFor } from '@testing-library/react' test('shows error when API fails', async () => { server.use( http.get('/api/users', () => { return HttpResponse.json({ error: 'Server Error' }, { status: 500 }) }) ) render(<UserList />) await waitFor(() => screen.getByText('Failed to load users')) })

Advanced Response Resolvers

import { http, HttpResponse, delay } from 'msw' export const handlers = [ // With artificial delay http.get('/api/slow', async () => { await delay(1500) return HttpResponse.json({ data: 'slow response' }) }), // Custom headers http.get('/api/data', () => { return new HttpResponse(JSON.stringify({ items: [] }), { status: 200, headers: { 'Content-Type': 'application/json', 'X-Total-Count': '42', 'Cache-Control': 'no-cache', }, }) }), // Network error simulation http.get('/api/broken', () => { return HttpResponse.error() }), // Passthrough — don't mock this path http.get('/api/real-endpoint', ({ request }) => { return fetch(request) // pass through to real server }), // Read search params http.get('/api/search', ({ request }) => { const url = new URL(request.url) const q = url.searchParams.get('q') return HttpResponse.json([{ id: 1, title: `Result for ${q}` }]) }), ]

Playwright Integration

// playwright/fixtures.ts — MSW in Playwright via route mocking // Option 1: Use Playwright's built-in route mocking (recommended for E2E) test('mocks API with Playwright routes', async ({ page }) => { await page.route('/api/users', async route => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify([{ id: 1, name: 'Alice' }]), }) }) await page.goto('/users') await expect(page.getByText('Alice')).toBeVisible() }) // Option 2: Use msw with Playwright (experimental) // playwright/fixtures.ts import { createServer } from 'http' import { createMiddleware } from '@mswjs/http-middleware' import { handlers } from './mocks/handlers' export async function startMockServer() { const middleware = createMiddleware(...handlers) const server = createServer(middleware) await new Promise(resolve => server.listen(9090, resolve)) return server }