mrkeyoor.com_
Sun 20 Sept 11:48 UTC
npmTestingupdated 20 Sept 2026

@mswjs/interceptors review

@mswjs/interceptors 0.42.3 is the low-level network interception layer behind Mock Service Worker. Node tools can observe HTTP from built-in clients, global fetch, direct Undici, Axios, and Got at the socket layer; narrower classes cover fetch, XMLHttpRequest, ClientRequest, WebSocket, or raw TCP and TLS. Events expose Fetch API Request objects and controllers that can supply Response objects. The current patch preserves initial bytes when a socket changes type. It is infrastructure for authors of mocking and traffic tools, not a route-matching test API.

Verdict

@mswjs/interceptors 0.42.3 installed in 2.7 seconds with zero audit findings, and our full browser import was 7.9 KB gzipped. Install it to build network tooling; ordinary application tests should start with MSW, Nock, or Undici MockAgent.

We installed it

Lab card: what happened when we installed @mswjs/interceptorsScreenshot of @mswjs/interceptors documentation
Install✓ · 2.7s9 packages on disk · 3 MB
ImportESM import works · require() works · ESM package with exports map
Browser7.9 KBgzipped (21.5 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does @mswjs/interceptors install cleanly?

Yes. In a fresh container with an empty cache, npm install @mswjs/interceptors finished in 3 seconds, leaving 9 packages and 3 MB on disk. npm audit reported no known vulnerabilities.

How much does @mswjs/interceptors add to a browser bundle?

7.9 KB gzipped (21.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does @mswjs/interceptors work with both ESM and CommonJS?

Yes. Both import '@mswjs/interceptors' and require('@mswjs/interceptors') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does @mswjs/interceptors include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

@mswjs/interceptors or msw: which should you use?

msw: Use it for named request handlers and shared browser or Node API mocks. @mswjs/interceptors 0.42.3 installed in 2.7 seconds with zero audit findings, and our full browser import was 7.9 KB gzipped.

When should you not use @mswjs/interceptors?

Tests only need reusable API handlers. The project itself recommends msw for matching and routing.

API stability3/5apply, dispose, request and response events, and controller-based outcomes form a consistent model across the exported interceptors. The package is still 0.42.3, so minor releases may carry both API and low-level implementation changes. Socket and all-client HTTP coverage are useful additions, but a tool that imports these classes directly should pin its range and test Node upgrades.
Docs5/5The README documents the socket-level design, client coverage, Request cloning, same-listener response timing, exception conversion, initiator detection, presets, WebSocket forwarding, and teardown. It plainly directs most test authors to a higher-level library. The page is long because the API exposes process and protocol details, but its concrete examples cover the mistakes that would otherwise hang requests or leak interception across tests.
Maintenance5/5GitHub reports an unarchived repository pushed on July 25, 2026, with 681 stars and 35 open issues and pull requests. Version 0.42.3 fixes preservation of the first bytes during a socket-type transition, a defect specific to its current low-level design. Its role under MSW gives maintainers recurring real traffic across Node releases rather than an isolated test fixture.
Ecosystem4/5npm counted 22,781,598 downloads from August 19 through August 25, 2026. Much of that reach comes through MSW, while direct users are library authors working below route handlers. Bundled declarations, conditional exports, and working import and require paths help those authors. Recipes and assertions remain richer in MSW and Nock because this package deliberately stops before routing.

Use it if

  • You are implementing a mocking, recording, or network diagnostic library rather than a normal application test.
  • One hook must see HTTP issued by several Node client stacks, including direct Undici calls.
  • Intercepted traffic should use standard Request and Response representations.
  • Raw socket or WHATWG WebSocket connections need connection-level control.
Skip it if

Setup reality

We installed @mswjs/interceptors 0.42.3 in 2.7 seconds in a fresh Node 22 container. Nine packages used 3 MB. The package declares six direct dependencies, no peers, 2,088 KB unpacked, bundled TypeScript declarations, and an MIT license. npm audit returned zero findings. Metadata marks it ESM with an exports map, while both require() and ESM import worked in our runtime check.

The browser build completed at 21.5 KB minified and 7.9 KB gzipped. No credentials or config file are involved. Import a precise subpath such as /fetch or combine classes with BatchInterceptor, register listeners, then call apply(). Test teardown must remove listeners and call dispose(); otherwise patched process globals and old callbacks can survive into later cases. Browser and Node presets cover different clients, so use the preset for the executing environment.

A request listener may inspect traffic, edit headers, return a Response, or raise an error through its controller. Clone the Request before reading a body stream. A request can be claimed once, and this package supplies no route table or handler precedence. If listener code is becoming a router, put MSW above it. Direct Undici calls require HttpRequestInterceptor rather than only the global FetchInterceptor.

respondWith must run within the listener's work. Awaited operations are supported because the interceptor follows the returned promise; a free-standing setTimeout callback arrives too late. Listener exceptions become synthetic 500 responses unless unhandledException rethrows them. Node interception changes process-level network primitives, so parallel tests in one process need exact matching to avoid handling each other's calls.

Patterns

Watch all Node HTTP traffic observe-node-http

import {HttpRequestInterceptor} from '@mswjs/interceptors/http'
const interceptor = new HttpRequestInterceptor()
interceptor.on('request', ({request}) => console.log(request.method, request.url))
interceptor.apply()

This class sees multiple client stacks at the socket layer; add client-specific interceptors when initiator identity matters.

Claim one fetch request return-json-response

import {FetchInterceptor} from '@mswjs/interceptors/fetch'
const interceptor = new FetchInterceptor()
interceptor.on('request', ({request, controller}) => {
  if (new URL(request.url).pathname === '/api/user') controller.respondWith(Response.json({id: 1}))
})
interceptor.apply()

Unclaimed requests continue to the network. URL matching and handler priority are your responsibility at this layer.

Await latency inside a handler delay-mock-response

import {setTimeout} from 'node:timers/promises'
interceptor.on('request', async ({controller}) => {
  await setTimeout(500)
  controller.respondWith(new Response(null, {status: 503}))
})

Await the delay in the listener. A detached timer fires after the interceptor has closed the response opportunity.

Clone a request before parsing JSON inspect-json-body

interceptor.on('request', async ({request, controller}) => {
  const body = await request.clone().json()
  if (body.action === 'delete') controller.respondWith(new Response(null, {status: 403}))
})

Request bodies are streams; clone preserves the original body for a request that ultimately passes through.

Mutate a request header add-outgoing-header

interceptor.on('request', ({request}) => {
  request.headers.set('x-trace-id', crypto.randomUUID())
})

Headers can change, but URL, method, and body cannot. Choose proxy software when those fields require rewriting.

Fail the test on listener exceptions surface-handler-error

interceptor.on('unhandledException', ({error}) => { throw error })

Without this listener, an exception in request handling is converted to a mocked HTTP 500 response.

Remove hooks after each suite manage-test-lifecycle

beforeAll(() => interceptor.apply())
afterEach(() => interceptor.removeAllListeners())
afterAll(() => interceptor.dispose())

Clearing listeners prevents handler carryover; dispose restores the process APIs patched by apply.

Answer a WebSocket ping locally intercept-websocket

import {WebSocketInterceptor} from '@mswjs/interceptors/WebSocket'
const interceptor = new WebSocketInterceptor()
interceptor.on('connection', ({client}) => {
  client.addEventListener('message', event => {
    if (event.data === 'ping') { event.preventDefault(); client.send('pong') }
  })
})
interceptor.apply()

The real server is not opened until server.connect(); preventDefault stops forwarding for the selected event.

Take ownership of one TCP host claim-raw-socket

interceptor.on('connection', ({socket, connectionOptions, controller}) => {
  if (connectionOptions.host === 'db.internal') controller.claim()
  else controller.passthrough()
})

Every socket must be claimed or passed through. Leaving it undecided keeps that connection pending.

Alternatives

PackageRegistryPick it when
mswnpmUse it for named request handlers and shared browser or Node API mocks.
nocknpmUse it for chainable Node HTTP expectations and matching.
undicinpmUse MockAgent when every request under test already uses Undici.

More testing guides

pytest · chai · vitest · jsdom · playwright · coverage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.