// After
import { http, HttpResponse } from 'msw/http'
import { graphql } from 'msw/graphql'
import { delay } from 'msw/utils/delay'
import { setupServer } from 'msw/node'
import { HttpNetworkFrame } from 'msw/experimental'
const server = setupServer(
http.get('/user', async () => {
await delay()
return HttpResponse.json({ name: 'John' })
}),
graphql.link('*').query('GetUser', () => HttpResponse.json({ data: {} })),
)
server.listen({
onUnhandledFrame({ frame, defaults }) {
if (!(frame instanceof HttpNetworkFrame)) {
return
}
const request = frame.data.request
console.log(request.url)
defaults.warn()
},
})
Important Notes
⚠️ Unhandled WebSocket connections: The migrated onUnhandledFrame callback only handles HTTP frames. In v2, the callback received a synthetic Request for unhandled WebSocket connections. Extend the frame instanceof HttpNetworkFrame check if you need to react to WebSocket frames.
⚠️ Custom onUnhandledRequest values: A callback passed by reference (onUnhandledRequest: handleUnhandled, including a function defined in another file) is rewritten when that function is only used as the listen or start option. A function that is also called elsewhere is left unchanged, and those call sites still need a manual update to the ({ frame, defaults }) signature.
⚠️ worker.stop(): The codemod awaits stop() when the receiver's definition is setupWorker(), including a binding imported from another file. A variable that is only named worker is left alone. Statements that already return or await the call are left as they are. A generator, constructor, or accessor is left alone because it cannot be made async.
⚠️ graphql peer dependency: graphql is now an optional peer dependency. Install it if you use msw/graphql.
Manual Migration
The following changes cannot be safely automated. Review them after running the codemod:
MSW v3 is ESM-only and requires Node.js 22+ and TypeScript 5.9+.
Run npx msw init to (re)generate the worker script. The postinstall hook is removed.
handleRequest() is removed. Use the defineNetwork() API from msw/experimental instead.
onUnhandledRequest(), UnhandledRequestStrategy, UnhandledRequestCallback, LifeCycleEventsMap, SetupApi, and StrictResponse are no longer exported.
request.headers.get('cookie') returns null in the handlers. Use the cookies resolver argument instead.
Cookies are no longer managed in Node.js. The default behavior of your environment applies.
bypass() removes the Content-Length request header.
setTimeout is no longer patched. Advance fake timers for delayed mocked responses to resolve.
file:// requests and HTTP-to-WebSocket upgrade requests via fetch() throw in Node.js.