Gathering detailed insights and metrics for enhanced-mswjs-interceptors
Gathering detailed insights and metrics for enhanced-mswjs-interceptors
npm install enhanced-mswjs-interceptors
Typescript
Module System
Min. Node Version
Node Version
NPM Version
73.3
Supply Chain
96.6
Quality
74.9
Maintenance
100
Vulnerability
100
License
TypeScript (97.19%)
JavaScript (2.81%)
Total Downloads
1,635
Last Day
1
Last Week
1
Last Month
26
Last Year
449
6 Commits
1 Watching
1 Branches
1 Contributors
Minified
Minified + Gzipped
Latest Version
0.18.0
Package Id
enhanced-mswjs-interceptors@0.18.0
Unpacked Size
390.31 kB
Size
86.64 kB
File Count
180
NPM Version
8.6.0
Node Version
18.0.0
Publised On
04 Sept 2023
Cumulative downloads
Total Downloads
Last day
0%
1
Compared to previous day
Last week
-91.7%
1
Compared to previous week
Last month
62.5%
26
Compared to previous month
Last year
-62.1%
449
Compared to previous year
8
32
@mswjs/interceptors
Low-level HTTP/HTTPS/XHR/fetch request interception library.
Intercepts any requests issued by:
http.get
/http.request
https.get
/https.request
XMLHttpRequest
window.fetch
axios
, request
, node-fetch
, supertest
, etc.)While there are a lot of network communication mocking libraries, they tend to use request interception as an implementation detail, giving you a high-level API that includes request matching, timeouts, retries, and so forth.
This library is a strip-to-bone implementation that provides as little abstraction as possible to execute arbitrary logic upon any request. It's primarily designed as an underlying component for high-level API mocking solutions such as Mock Service Worker.
A traditional API mocking implementation in Node.js looks roughly like this:
1import http from 'http' 2 3function applyMock() { 4 // Store the original request module. 5 const originalHttpRequest = http.request 6 7 // Rewrite the request module entirely. 8 http.request = function (...args) { 9 // Decide whether to handle this request before 10 // the actual request happens. 11 if (shouldMock(args)) { 12 // If so, never create a request, respond to it 13 // using the mocked response from this blackbox. 14 return coerceToResponse.bind(this, mock) 15 } 16 17 // Otherwise, construct the original request 18 // and perform it as-is (receives the original response). 19 return originalHttpRequest(...args) 20 } 21}
This library deviates from such implementation and uses class extensions instead of module rewrites. Such deviation is necessary because, unlike other solutions that include request matching and can determine whether to mock requests before they actually happen, this library is not opinionated about the mocked/bypassed nature of the requests. Instead, it intercepts all requests and delegates the decision of mocking to the end consumer.
1class NodeClientRequest extends ClientRequest { 2 async end(...args) { 3 // Check if there's a mocked response for this request. 4 // You control this in the "resolver" function. 5 const mockedResponse = await resolver(isomorphicRequest) 6 7 // If there is a mocked response, use it to respond to this 8 // request, finalizing it afterward as if it received that 9 // response from the actual server it connected to. 10 if (mockedResponse) { 11 this.respondWith(mockedResponse) 12 this.finish() 13 return 14 } 15 16 // Otherwise, perform the original "ClientRequest.prototype.end" call. 17 return super.end(...args) 18 } 19}
By extending the native modules, this library actually constructs requests as soon as they are constructed by the consumer. This enables all the request input validation and transformations done natively by Node.js—something that traditional solutions simply cannot do (they replace http.ClientRequest
entirely). The class extension allows to fully utilize Node.js internals instead of polyfilling them, which results in more resilient mocks.
This library extends (or patches, where applicable) the following native modules:
http.get
/http.request
https.get
/https.request
XMLHttpRequest
fetch
Once extended, it intercepts and normalizes all requests to the isomorphic request instances. The isomorphic request is an abstract representation of the request coming from different sources (ClientRequest
, XMLHttpRequest
, window.Request
, etc.) that allows us to handle such requests in the same, unified manner.
You can respond to an isomorphic request using an isomorphic response. In a similar way, the isomorphic response is a representation of the response to use for different requests. Responding to requests differs substantially when using modules like http
or XMLHttpRequest
. This library takes the responsibility for coercing isomorphic responses into appropriate responses depending on the request module automatically.
1npm install @mswjs/interceptors
To use this library you need to choose one or multiple interceptors to apply. There are different interceptors exported by this library to spy on respective request-issuing modules:
ClientRequestInterceptor
to spy on http.ClientRequest
(http.get
/http.request
);XMLHttpRequestInterceptor
to spy on XMLHttpRequest
;FetchInterceptor
to spy on fetch
.Use an interceptor by constructing it and attaching request/response listeners:
1import { ClientRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/ClientRequest' 2 3const interceptor = new ClientRequestInterceptor() 4 5// Enable the interception of requests. 6interceptor.apply() 7 8// Listen to any "http.ClientRequest" being dispatched, 9// and log its method and full URL. 10interceptor.on('request', (request) => { 11 console.log(request.method, request.url.href) 12}) 13 14// Listen to any responses sent to "http.ClientRequest". 15// Note that this listener is read-only and cannot affect responses. 16interceptor.on('response', (response, request) => { 17 console.log( 18 'response to %s %s was:', 19 request.method, 20 request.url.href, 21 response 22 ) 23})
All HTTP request interceptors implement the same events:
request
, emitted whenever a request has been dispatched;response
, emitted whenever any request receives a response.You can combine multiple interceptors to capture requests from different request-issuing modules at once.
1import { BatchInterceptor } from '@mswjs/interceptors' 2import { ClientRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/ClientRequest' 3import { XMLHttpRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/XMLHttpRequest' 4 5const interceptor = new BatchInterceptor({ 6 name: 'my-interceptor', 7 interceptors: [ 8 new ClientRequestInterceptor(), 9 new XMLHttpRequestInterceptor(), 10 ], 11}) 12 13interceptor.apply() 14 15// This "request" listener will be called on both 16// "http.ClientRequest" and "XMLHttpRequest" being dispatched. 17interceptor.on('request', listener)
Note that you can use pre-defined presets that cover all the request sources for a given environment type.
When using BatchInterceptor
, you can provide a pre-defined preset to its "interceptors" option to capture all request for that environment.
This preset combines ClientRequestInterceptor
, XMLHttpRequestInterceptor
and is meant to be used in Node.js.
1import { BatchInterceptor } from '@mswjs/interceptors' 2import nodeInterceptors from '@mswjs/interceptors/lib/presets/node' 3 4const interceptor = new BatchInterceptor({ 5 name: 'my-interceptor', 6 interceptors: nodeInterceptors, 7}) 8 9interceptor.apply() 10 11interceptor.on('request', listener)
This preset combines XMLHttpRequestInterceptor
and FetchInterceptor
and is meant to be used in a browser.
1import { BatchInterceptor } from '@mswjs/interceptors' 2import browserInterceptors from '@mswjs/interceptors/lib/presets/browser' 3 4const interceptor = new BatchInterceptor({ 5 name: 'my-interceptor', 6 interceptors: browserInterceptors, 7}) 8 9interceptor.on('request', listener)
All HTTP request interceptors emit a "request" event. In the listener to this event, they expose an isomorphic request
instance—a normalized representation of the captured request.
There are many ways to describe a request in Node.js, that's why this library exposes you a custom request instance that abstracts those details away from you, making request listeners uniform.
1interceptor.on('reqest', (request) => {})
The exposed request
partially implements Fetch API Request specification, containing the following properties and methods:
1interface IsomorphicRequest { 2 id: string 3 url: URL 4 method: string 5 headers: Headers 6 credentials: 'omit' | 'same-origin' | 'include' 7 bodyUsed: boolean 8 clone(): IsomorphicRequest 9 arrayBuffer(): Promise<ArrayBuffer> 10 text(): Promise<string> 11 json(): Promise<Record<string, unknown>> 12}
For example, this is how you would read a JSON request body:
1interceptor.on('request', async (request) => { 2 const json = await request.json() 3})
Although this library can be used purely for request introspection purposes, you can also affect request resolution by responding to any intercepted request within the "request" event.
Use the request.respondWith()
method to respond to a request with a mocked response:
1interceptor.on('request', (request) => { 2 request.respondWith({ 3 status: 200, 4 statusText: 'OK', 5 headers: { 6 'Content-Type': 'application/json', 7 }, 8 body: JSON.stringify({ 9 firstName: 'John', 10 lastName: 'Maverick', 11 }), 12 }) 13})
Note that a single request can only be handled once. You may want to introduce conditional logic, like routing, in your request listener but it's generally advised to use a higher-level library like Mock Service Worker that does request matching for you.
Requests must be responded to within the same tick as the request listener. This means you cannot respond to a request using setTimeout
, as this will delegate the callback to the next tick. If you wish to introduce asynchronous side-effects in the listener, consider making it an async
function, awaiting any side-effects you need.
1// Respond to all requests with a 500 response 2// delayed by 500ms. 3interceptor.on('request', async (request) => { 4 await sleep(500) 5 request.respondWith({ status: 500 }) 6})
Interceptor
A generic class implemented by all interceptors. You do not interact with this class directly.
1class Interceptor { 2 // Applies the interceptor, enabling the interception of requests 3 // in the current process. 4 apply(): void 5 6 // Listens to the public interceptor events. 7 // For HTTP requests, these are "request' and "response" events. 8 on(event, listener): void 9 10 // Cleans up any side-effects introduced by the interceptor 11 // and disables the interception of requests. 12 dispose(): void 13}
For public consumption, use interceptors instead.
BatchInterceptor
Applies multiple request interceptors at the same time.
1import { BatchInterceptor } from '@mswjs/interceptors' 2import nodeInterceptors from '@mswjs/interceptors/lib/presets/node' 3 4const interceptor = new BatchInterceptor({ 5 name: 'my-interceptor', 6 interceptors: nodeInterceptors, 7}) 8 9interceptor.apply() 10 11interceptor.on('request', (request) => { 12 // Inspect the intercepted "request". 13 // Optionally, return a mocked response. 14})
Using the
/presets/node
interceptors preset is the recommended way to ensure all requests get intercepted, regardless of their origin.
RemoteHttpInterceptor
Enables request interception in the current process while delegating the response resolution logic to the parent process. Requires the current process to be a child process. Requires the parent process to establish a resolver by calling the createRemoteResolver
function.
1// child.js 2import { RemoteHttpInterceptor } from '@mswjs/interceptors/lib/RemoteHttpInterceptor' 3import { ClientRequestInterceptor } from '@mswjs/interceptors/lib/interceptors/ClientRequest' 4 5const interceptor = new RemoteHttpInterceptor({ 6 // Alternatively, you can use presets. 7 interceptors: [new ClientRequestInterceptor()], 8}) 9 10interceptor.apply() 11 12process.on('disconnect', () => { 13 interceptor.dispose() 14})
You can still listen to and handle any requests in the child process via the request
event listener. Keep in mind that a single request can only be responded to once.
RemoteHttpResolver
Resolves an intercepted request in the given child process
. Requires for that child process to enable request interception by calling the createRemoteInterceptor
function.
1// parent.js 2import { spawn } from 'child_process' 3import { RemoteHttpResolver } from '@mswjs/interceptors/lib/RemoteHttpInterceptor' 4 5const appProcess = spawn('node', ['app.js'], { 6 stdio: ['inherit', 'inherit', 'inherit', 'ipc'], 7}) 8 9const resolver = new RemoteHttpResolver({ 10 process: appProcess, 11}) 12 13resolver.on('request', (request) => { 14 // Optionally, return a mocked response 15 // for a request that occurred in the "appProcess". 16})
The following libraries were used as an inspiration to write this low-level API:
No vulnerabilities found.
No security vulnerabilities found.