Gathering detailed insights and metrics for onfetch
Gathering detailed insights and metrics for onfetch
Gathering detailed insights and metrics for onfetch
Gathering detailed insights and metrics for onfetch
Mock fetch() with native Request / Response API. Works with globalThis, service worker, @mswjs/interceptors, and custom contexts
npm install onfetch
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
121 Commits
1 Watching
3 Branches
1 Contributors
Updated on 17 Jan 2022
TypeScript (99.26%)
JavaScript (0.64%)
HTML (0.1%)
Cumulative downloads
Total Downloads
Last day
0%
1
Compared to previous day
Last week
-50%
1
Compared to previous week
Last month
-39.5%
26
Compared to previous month
Last year
-38%
519
Compared to previous year
2
Mock fetch()
with native Request
/ Response
API. Works with globalThis
, service worker, @mswjs/interceptors
, and custom contexts.
🐿️ Jump to Callback, Delay, Redirect, Times, Restore, Context, Options, Q&A, or Contributing Guide.
Start with onfetch
, pass the same params as constructing a Request
object. Then reply
as constructing a Response
object.
1import onfetch from 'onfetch'; 2 3onfetch('/simple').reply('path'); 4 5// Or 6onfetch('/post', { method: 'POST' }) 7 .reply('received'); 8 9// Or 10onfetch('/down') 11 .reply(null, { status: 500 });
Works with node-fetch
, whatwg-fetch
, cross-fetch
, whatever, and mainly, modern browsers.
In Node, in addition to setting up global fetch
, you also need to set up global Headers
, Request
, and Response
.
How onfetch
uses your params to match a Request
.
To keep this simple and efficient, we don't and won't support body
matching. You will have to put your own processing code into a reply callback when needed.
Rules without a positive times
match no request.
A string matches the request's URL if all the following checks pass:
1onfetch('/string').persist(); 2 3fetch('/string'); // match 4fetch('/string?query'); // match 5fetch('/string#hash'); // match
1onfetch('/string?specify-query').persist(); 2 3fetch('/string'); // not match 4fetch('/string?other-query'); // not match 5fetch('/string?specify-query'); // match 6fetch('/string?specify-query#hash'); // match
The use of persist()
allows the above onfetch
rules to match an unlimited number of times.
The second param, a RequestInit
object, matches the Request
, when all the checks in the following steps pass:
headers
, body
, window
and the rest parts from the RequestInit
object.headers
has a match in the request's headers.Request
.1onfetch('', { 2 method: 'GET', 3 headers: { 4 'Content-Type': 'text/plain', 5 }, 6}).persist(); 7 8// not match 9fetch('', { method: 'GET' }); 10 11// not match 12fetch('', { 13 method: 'POST', 14 headers: { 15 'Content-Type': 'text/plain', 16 }, 17}); 18 19// match 20fetch('', { 21 cache: 'no-cache', 22 method: 'GET', 23 headers: { 24 'Accept': 'text/html', 25 'Content-Type': 'text/plain', 26 }, 27});
test
methodOther than using strings, you can also pass any object that has a test
method as the first arg to test the request's URL.
The test
method will receive the full URL string and should return a boolean indicating the match result.
An example that uses a RegExp
:
1// Match URLs that ends with '.foo'. 2onfetch(/\.foo$/).reply('bar');
Because it tests the entire URL string, if this onfetch
rule doesn't need to care about the query string or the hash, write it like:
1// Use regex that 2// allows any trailing query string and hash. 3onfetch(/^[^?#]*\.foo([?#]|$)/).reply('bar');
Another example that uses a URLPattern
:
1const pattern = new URLPattern('http{s}?://*.example.com/books/:id');
2onfetch(pattern);
You can also pass a Request
object as the first arg, to match the request in a manner similar with the RequestInit
matcher.
Other than reply
as constructing a Response
, you can also pass a callback function to form the response.
Your callback will receive two params, the first one points to the Request
object, the second one gives you both the original and the mocked fetch
.
Remember to return a Response
, BodyInit
, null
, or a Promise
that resolves to one of them.
1onfetch('').reply((request, fetchers) => { 2 const example = request.headers.get('One'); 3 if (example === 'original') { 4 return fetchers.original(request); 5 } 6 if (example === 'mocked') { 7 return fetchers.mocked('/mocked'); 8 } 9 return 'default-response'; 10});
A syntactic sugar for sending requests via the original fetch
.
1import onfetch, { passThrough } from 'onfetch'; 2onfetch('/use-original').reply(passThrough);
1// Delay 200ms before reply. 2onfetch('').delay(200).reply('');
The order of delay
and reply
does not affect the result.
1// Same effect. 2onfetch('').reply('').delay(200);
The delay duration accumulates.
1// Delay 400ms before reply. 2onfetch('').delay(200).delay(300).delay(-100).reply('');
Use a Response
object that has a redirect status to redirect requests. You can use Response.redirect
to construct a such object.
1// Redirect to '/bar'. 2onfetch('/foo').reply(Response.redirect('/bar')); 3 4// `/bar` respond with `redirected`. 5onfetch('/bar').reply('redirected'); 6 7// Logs 'redirected' 8fetch('/foo').then((res) => res.text()).then(console.log);
Request
with redirect
set to a value other than follow
will fail the fetch with a TypeError
.Response
only has the correct redirected
and url
properties defined on the response object itself. Reading them via the prototype will give you incorrect values.You can specify the number of times to apply the onfetch
rule via the times
function. It accepts an integer as the number of applications of the rule.
1// Apply this rule 5 times. 2onfetch('/foo').times(5).reply('bar');
You may have multiple rules matching a request at the same time, but only the first rule will apply.
By default, an onfetch
rule only applies once. When the times ran out, it bypasses the match.
1onfetch('/foo').reply('alpha'); 2onfetch('/foo').reply('beta'); 3 4// Logs 'alpha' 5fetch('/foo').then((res) => res.text()).then(console.log); 6 7// Logs 'beta' 8fetch('/foo').then((res) => res.text()).then(console.log);
You can specify the times at any time as long as you store the reference of the onfetch
rule somewhere.
1const onFoo = onfetch('/foo').reply('bar'); 2 3fetch('/foo'); // match 4 5// Once again. 6onFoo.once(); 7 8fetch('/foo'); // match
Note that when all the onfetch
rules do not match a request, that request goes to options.defaultRule
.
The times(n)
doesn't accumulate. It overrides.
1const onFoo = onfetch('/foo').twice().once().reply('bar'); 2 3fetch('/foo'); // match 4fetch('/foo'); // fallback to `defaultRule`
once()
A syntactic sugar for rule.times(1)
.
twice()
Syntactic sugar for rule.times(2)
.
thrice()
Sugar for rule.times(3)
.
persist()
For rule.times(Infinity)
.
restore
deactivates onfetch
to stop intercepting HTTP calls. Note that it does not clear any intercept rules.
1onfetch.restore();
1const first = onfetch('/same').reply('first'); 2onfetch('/same').reply('second'); 3 4onfetch.remove(first); 5 6// Logs 'second' 7fetch('/foo').then((res) => res.text()).then(console.log);
1onfetch.cleanAll();
To (re-)activate onfetch
to start intercepting HTTP calls, you can use activate()
.
onfetch
activates itself when you first import it.
1onfetch.restore(); 2 3// After some code. 4 5onfetch.activate();
In the default mode, onfetch
intercepts calls to the fetch()
method on globalThis
.
To switch back from another mode to this, run:
1await onfetch.useDefault();
Service Worker API only works in browsers.
With the help of Service Worker API, you can now mock all the resources your page requires, including those don't go with XMLHttpRequest nor fetch
(e.g., CSS files).
1// In the main browsing context. 2import onfetch from 'onfetch'; 3 4await onfetch.useServiceWorker(); 5 6onfetch('/script.js').reply('console.log(\'mocked!\')'); 7const script = document.createElement('script'); 8script.src = '/script.js'; 9 10// Logs 'mocked!' 11document.head.append(script);
To enable this feature, import onfetch/sw
in your service worker.
1// In the service worker. 2import 'onfetch/sw';
To switch back to the standard mode in the client side, call onfetch.useDefault()
.
To disable onfetch/sw
in the worker side, store its default import value beforehand, and call its restore
method.
1// In the service worker. 2import onfetchWorker from 'onfetch/sw'; 3 4self.addEventListener('message', async ({ data }) => { 5 // To re-activate, call `.activate()`. 6 if (data?.example) await onfetchWorker.restore(); 7 8 // ... 9});
@mswjs/interceptors
is a HTTP/HTTPS/XHR/fetch request interception library that can intercept most of the requests in Node.js.
With the help of @mswjs/interceptors
, we can mock almost all the resources our Node tests requires. To install, run:
1npm i @mswjs/interceptors --save-dev
Then somewhere in your test cases, call useMSWInterceptors
.
1// Switch to @mswjs/interceptors mode. 2await onfetch.useMSWInterceptors();
Auto Advanced mode uses either service worker mode or msw interceptors mode depending on whether the env supports the Service Worker API.
1// Switch to service worker mode, if the env supports it. 2// Otherwise, switch to @mswjs/interceptors mode. 3await onfetch.useAutoAdvanced();
onfetch
works by replacing the fetch
property of a given "context" with a mocked one. By default, this context refers to globalThis
. The service worker mode and msw interceptors mode integrates with onfetch
by transmitting requests to the fetch()
method of an object and passing this object to onfetch
as the "context". By doing so onfetch
can intercept the requests.
You can write a custom context like:
1class SimpleContext { 2 fetch = async () => new Response('original'); 3} 4 5const context = new SimpleContext();
Then use onfetch.adopt()
to let onfetch
use this context
.
1await onfetch.adopt(context);
Now, all the accesses to the context
's fetch()
method get intercepted.
1onfetch('/basic').reply('mocked'); 2onfetch('/bypass').reply(passThrough); 3 4// Logs 'mocked' 5context.fetch('/basic') 6 .then((res) => res.text()) 7 .then(console.log); 8 9// Logs 'original' 10context.fetch('/bypass') 11 .then((res) => res.text()) 12 .then(console.log);
In default mode:
Configurable via onfetch.config
.
The rule used when all onfetch
rules failed to match a request. You can form a rule by constructing a InterceptRule
object, which accepts the same params as onfetch
.
1import onfetch, { InterceptRule } from 'onfetch'; 2onfetch.config({ 3 defaultRule: new InterceptRule('').reply('default'), 4});
Defaults to:
1new InterceptRule('').reply((req) => {
2 throw new Error(`No onfetch rule matches this request to '${req.url}'`);
3})
Constructor for abort errors. It should extend from Error
and its instance should have the name
property set to AbortError
.
1import onfetch from 'onfetch'; 2onfetch.config({ 3 AbortError: PutItHere, 4});
In Browsers, defaults to:
1DOMException.bind(null, 'The user aborted a request.', 'AbortError');
In Node, defaults to:
1class AbortError extends Error { 2 name = 'AbortError'; 3 4 constructor() { 5 super('The user aborted a request.'); 6 } 7}
Set this to true
to bypass onfetch
redirection.
1import onfetch from 'onfetch'; 2onfetch.config({ 3 bypassRedirect: true, // or false 4});
In advanced modes, this defaults to true
, as the browser / upstream package will handle the redirections on its own (see request flow). So we can overcome some redirect limitations.
Checkout our Q&A Discussions for your answers. 👍
Checkout our Contributing Guide please. 👍
No vulnerabilities found.
No security vulnerabilities found.