|claim|login
RepoCritics — Review. Share. Archive. Every open-source repo.

mswjs/msw

Wiki: mswjs/msw

Source: https://github.com/mswjs/msw

Last synced 2026-07-16 · 1107 words · Edit wiki on GitHub →

mswjs/msw

> Network-level API mocking that intercepts requests after they leave your app, using a Service Worker in the browser and low-level request interception in Node.

GitHub repo · Official website · License: MIT

Overview

Mock Service Worker (MSW) is an API mocking library for JavaScript, first published in 2019 by Artem Zakharchenko (kettanaito)1. Its defining idea is that mocks live outside your application code: instead of stubbing fetch, axios, or XMLHttpRequest, MSW intercepts requests at the network boundary — after they have actually been dispatched. Your application runs its real request-making code and does not know mocking is happening. The same request handlers can be reused across unit tests, integration tests, E2E, and local development.

In the browser MSW registers a Service Worker that intercepts outgoing requests and replies from your handlers. In Node.js — where Service Workers do not exist — it swaps in a low-level interception layer (@mswjs/interceptors) that patches http/https, XMLHttpRequest, and native fetch/undici, running the identical handlers2. This dual-runtime design is the whole selling point and also the source of most of its operational complexity: two very different interception mechanisms have to present one API.

The library's central tension is fidelity versus setup burden. Because it operates at the network level, MSW gives higher confidence than function-level stubs, but it demands a real Service Worker asset, a secure context, and correct test lifecycle wiring — more moving parts than a jest.mock(). The 1.x→2.x rewrite (2023) traded a bespoke response API for standard Fetch Request/Response objects and a Node 18+ requirement, which was the single largest disruption in the project's history3.

Getting Started

npm install msw --save-dev
# Browser only: generate the Service Worker asset into your public dir
npx msw init public/ --save
// handlers.js — shared across browser and Node
import { http, HttpResponse } from 'msw'

export const handlers = [
  http.get('https://api.example.com/user', () => {
    return HttpResponse.json({ id: 1, name: 'Ada' })
  }),
]
// Node / Vitest / Jest test setup
import { setupServer } from 'msw/node'
import { handlers } from './handlers'

const server = setupServer(...handlers)

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

Architecture / How It Works

MSW has two interception backends behind one handler API (http.*, graphql.*, ws.*):

1. Browser (msw/browser, setupWorker)npx msw init copies a static mockServiceWorker.js into your public directory. At runtime the worker registers, claims the page scope, and forwards every intercepted request over postMessage to the client thread, where your handlers actually run. This is why handlers can use TypeScript, closures, and third-party libraries: the worker only signals; resolution happens on the client4. 2. Node (msw/node, setupServer) — no Service Worker exists, so MSW uses @mswjs/interceptors to patch the request primitives (ClientRequest, XMLHttpRequest, and Node's native fetch/undici). The same handler array is evaluated against the intercepted request2.

Handlers use Express-like path syntax with params, wildcards, and regex. Since 2.x, a resolver receives { request, params, cookies } where request is a standard Request, and returns a standard Response (usually via the HttpResponse helper). Before 2.x the API was rest.get(path, (req, res, ctx) => res(ctx.json(...))) — a custom response composition model that predated stable platform Fetch primitives3.

Beyond REST, graphql.query/graphql.mutation match operations by name, and a ws handler (added in the 2.x line) intercepts WebSocket connections. msw/data is a separate optional package that layers a small in-memory data model with relationships on top of handlers when you want a stateful fake backend.

Production Notes

  • The Service Worker is a versioned static asset, not code you import. mockServiceWorker.js is generated by msw init and must match the installed library version. Upgrading the npm package without re-running msw init leaves a stale worker; MSW prints an integrity/version-mismatch warning. In CI and deploy pipelines you must either commit the worker or regenerate it during build.
  • Browser interception requires a secure context and correct scope. Service Workers only run over HTTPS or on localhost, and only intercept requests within their registration scope. Requests fired before await worker.start() resolves escape mocking entirely — start the worker before rendering to avoid boot-time race conditions.
  • Node test isolation is manual. Without server.resetHandlers() in afterEach, one-off handlers registered via server.use() leak into later tests; without server.close() in afterAll, interception stays patched. server.boundary() scopes handlers to a closure for concurrent/parallel work.
  • onUnhandledRequest default is noisy. The default 'warn' logs every un-mocked request; teams usually flip to 'error' to fail fast on missing mocks, or 'bypass' to allow real passthrough. Individual handlers can opt out with passthrough().
  • 1.x → 2.x is the dominant upgrade pain. rest became http; res(ctx.json()) became return HttpResponse.json(); the resolver signature changed; and Node 18+ with native fetch became mandatory. An official codemod and migration guide exist, but non-trivial handlers often need manual edits5.
  • jsdom/undici friction. Because 2.x depends on platform Fetch primitives (undici in Node), older jsdom-based test setups sometimes need the runtime configured to expose global fetch/Response/TextEncoder. Modern Vitest and recent Jest largely handle this, but it remains a common first-run snag.

When to Use / When Not

Use when:

  • You want one set of mock definitions reused across unit, integration, E2E, and local dev.
  • You want application code that is unaware of mocking (network-level fidelity, no adapter per HTTP client).
  • You mock REST and/or GraphQL (and optionally WebSocket) and like Express-style routing.
  • You value inspecting mocked responses in the browser DevTools Network tab.

Avoid when:

  • You need a standalone mock server that non-JS clients can hit — MSW is in-process, not a network service.
  • You want a fully stateful fake backend with relationships/persistence out of the box (MSW + msw/data is lighter than a dedicated tool).
  • Your browser target or CI environment lacks Service Worker support or cannot serve the worker asset.
  • You only mock fetch in Node and want a minimal dependency — a single-purpose stub is simpler.

Alternatives

  • nock/nock — Node-only HTTP interception by patching the http module; use it when you never touch the browser and want the long-established Node standard.
  • wheresrhys/fetch-mock — mocks the fetch API specifically; use it when fetch is your only client and you want a smaller surface than MSW.
  • miragejs/miragejs — in-browser mock server with an ORM-like data layer; use it when you want a stateful fake backend with relationships rather than per-request handlers.
  • stoplightio/prism — standalone mock HTTP server generated from an OpenAPI document; use it when you have a spec and need a real server any client can call.
  • wiremock/wiremock — JVM-based standalone mock service; use it when you need language-agnostic, contract-level mocking as an external process.

History

VersionDateNotes
0.x2019Initial public releases; browser Service Worker interception1.
1.02023Stability milestone on the original rest/res(ctx) API before the rewrite.
2.02023-10Major rewrite: standard Fetch Request/Response, http/HttpResponse/graphql API, Node 18+ required3.
2.x2024–2026WebSocket (ws) interception, ongoing interceptor and runtime updates.

References

  1. ^ Mock Service Worker — official site and documentation. https://mswjs.io
  2. ^ @mswjs/interceptors — low-level request interception library used by the Node integration. https://github.com/mswjs/interceptors
  3. ^ "Introducing MSW 2.0" — release announcement, October 2023. https://mswjs.io/blog/introducing-msw-2.0
  4. ^ MSW docs, "Integrations — Browser" and setupWorker API. https://mswjs.io/docs/integrations/browser
  5. ^ MSW docs, "Migrating to 2.x". https://mswjs.io/docs/migrations/1.x-to-2.x

Tags

typescript, javascript, api-mocking, service-worker, testing, mock, rest, graphql, node, browser, devtools