协作头脑风暴
brainstorming
在实现前澄清需求、探索设计选择,并形成清晰的开发方向。
msw【开发与工程】Intercepts network requests to mock REST, GraphQL, SSE and WebSocket APIs with Mock Service Worker (MSW). Use when mocking APIs for unit tests, integration tests, Storybook or local development without changing application code or running a mock server. Trigger words: msw, mock service worker, api mocking, request handlers, setupServer, setupWorker.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
MSW intercepts requests at the network level: through a service worker in the browser and through request interception in Node.js. The same handlers serve tests, Storybook and local development, and they work with any client (fetch, axios, Apollo) without touching application code. Current release is 3.0 (September 2026). It is ESM-only, needs Node.js 22.12+ and TypeScript 5.9+, and changed several APIs from 2.x. Check npm ls msw first: the examples below are 3.x, and the 2.x differences are listed in Guidelines.
npm install msw --save-dev. Install graphql too if you mock GraphQL (it is now an optional peer dependency).http.get(), http.post() and so on, imported from msw/http (the msw root still re-exports http, HttpResponse and delay). Return HttpResponse.json(body, { status }). In Node.js use absolute URLs; relative ones only resolve in the browser.const api = graphql.link('https://api.shopwave.dev/graphql'), then api.query('GetPosts', resolver) and api.mutation('CreatePost', resolver). graphql comes from msw/graphql; the 2.x form graphql.query(...) no longer exists.setupServer(...handlers) from msw/node. Call server.listen() in beforeAll, server.resetHandlers() in afterEach, server.close() in afterAll. listen({ onUnhandledFrame: 'error' }) fails tests on any request without a handler (default is 'warn'; 'bypass' is silent). This option was called onUnhandledRequest in 2.x.setupWorker(...handlers) from msw/browser and run npx msw init ./public --save once to copy mockServiceWorker.js. Start it with await worker.start() before rendering the app, only in development.msw/vite plugin serves the worker script itself: add msw() (import { msw } from 'msw/vite') to plugins, then in src/main.ts call const { network } = await import('virtual:msw'); network.configure({ handlers }); await network.enable() inside if (import.meta.env.DEV). It works for SSR too and is left out of production builds.api.subscription('OnCreateMessage', ({ subscription }) => subscription.publish({ data })); failed WebSocket connections emit websocket:error.server.use(...); resetHandlers() removes the overrides again.await delay(300) (from msw/utils/delay or msw), HttpResponse.error() for a network failure, and non-2xx statuses for server errors.server.events.on('request:start', ({ request }) => console.log(request.method, request.url)).User request: "Mock my users API in Vitest so the list page can be tested with happy and error paths"
// src/mocks/handlers.ts
import { http, HttpResponse } from 'msw/http'
export const handlers = [
http.get('http://localhost:3000/api/users', () =>
HttpResponse.json([{ id: 'u_101', name: 'Maya Chen' }, { id: 'u_102', name: 'Dmitri Volkov' }]),
),
http.post('http://localhost:3000/api/users', async ({ request }) => {
const body = await request.json()
return HttpResponse.json({ id: 'u_103', ...body }, { status: 201 })
}),
]
// src/mocks/node.ts
import { setupServer } from 'msw/node'
import { handlers } from './handlers'
export const server = setupServer(...handlers)
// vitest.setup.ts (listed under setupFiles in vitest.config.ts)
import { beforeAll, afterEach, afterAll } from 'vitest'
import { server } from './src/mocks/node'
beforeAll(() => server.listen({ onUnhandledFrame: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
In the error-state test, add server.use(http.get('http://localhost:3000/api/users', () => HttpResponse.json(null, { status: 500 }))) before rendering.
Result: npx vitest run passes; the happy-path test sees two users, the error test sees the 500, and an accidental call to an unmocked URL fails with [MSW] Error: intercepted a request without a matching request handler.
User request: "Run my Vite app against a fake GraphQL backend with a loading delay"
// src/mocks/handlers.ts
import { HttpResponse } from 'msw/http'
import { graphql } from 'msw/graphql'
import { delay } from 'msw/utils/delay'
const api = graphql.link('https://api.shopwave.dev/graphql')
export const handlers = [
api.query('GetPosts', async () => {
await delay(300)
return HttpResponse.json({ data: { posts: [{ id: 1, title: 'Launch notes' }] } })
}),
api.mutation('CreatePost', ({ variables }) =>
HttpResponse.json({ data: { createPost: { id: 2, title: variables.title } } }),
),
]
Add msw() from msw/vite to vite.config.ts, then enable the network in src/main.ts as described above and run npm run dev.
Result: The browser console prints [MSW] Mocking enabled., and GetPosts and CreatePost calls appear in DevTools with a 300 ms delay on the query.
msw/native is gone; worker.stop() returns a Promise; graphql.query became graphql.link(url).query; onUnhandledRequest became onUnhandledFrame; the WebSocket connection event is websocket:connection; LifeCycleEventsMap is replaced by HttpNetworkFrameEventMap and WebSocketNetworkFrameEventMap from msw/experimental; handleRequest is replaced by getResponse(handlers, request); bypass, passthrough and delay now live in msw/utils/*.msw@2; do not mix imports between versions.src/mocks/handlers.ts, error states in server.use() inside the test.mockServiceWorker.js (re-run npx msw init after upgrading, or use the Vite plugin).server.listen() call without resetHandlers() leaks overrides between tests.