I want to intercept fetch API requests and responses in JavaScript.
For example, before sending the request I want to intercept the request URL. I'd like to intercept the response once it arrives as well.
The below code is for intercepting responses of all XMLHTTPRequest
s.
(function(open) {
XMLHttpRequest.prototype.open = function(XMLHttpRequest) {
var self = this;
this.addEventListener("readystatechange", function() {
if (this.responseText.length > 0 &&
this.readyState == 4 &&
this.responseURL.indexOf('www.google.com') >= 0) {
Object.defineProperty(self, 'response', {
get: function() { return bValue; },
set: function(newValue) { bValue = newValue; },
enumerable: true,
configurable: true
});
self.response = 'updated value' // Intercepted Value
}
}, false);
open.apply(this, arguments);
};
})(XMLHttpRequest.prototype.open);
I want to implement the same feature for fetch()
API. How can I do this?
To intercept HTTP requests, use the webRequest API. This API enables you to add listeners for various stages of making an HTTP request. In the listeners, you can: Get access to request headers and bodies and response headers.
Interceptors are code blocks that you can use to preprocess or post-process HTTP calls, helping with global error handling, authentication, logging, and more. In this article, you'll learn how to intercept JavaScript Fetch API calls.
The Fetch API provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as requests and responses. It also provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.
The fetch() method returns a promise that can either be resolved or not. Thus, this method always returns a value and the only possible case where it may fail to return anything is a server error.
Existing answers show the general structure for mocking fetch
in the browser but omit important details.
The accepted answer shows the general pattern for replacing the window.fetch
function with custom implementation that intercepts the call and forwards the arguments to fetch
. However, the pattern shown doesn't let the interception function do anything with the response (for example, read the status or body or inject a mock) so is only useful for logging request parameters. This is a pretty narrow use case.
This answer uses an async
function to let the interceptor await
on the fetch
promise and presumably work with the response (mocking, reading, etc) but (at the time of writing) has a superfluous closure and doesn't show how to read the response body non-destructively. It also contains a variable aliasing bug leading to a stack overflow.
This answer is the most complete so far but has some irrelevant noise in the callback and doesn't mention anything about cloning the response to enable the body to be collected by the interceptor. It doesn't illustrate how a mock could be returned.
Here's a minimal, complete example that rectifies these issues, showing how to handle parameter logging, reading the body without harming the original caller by cloning the response and (optionally) providing a mock response.
const {fetch: origFetch} = window;
window.fetch = async (...args) => {
console.log("fetch called with args:", args);
const response = await origFetch(...args);
/* work with the cloned response in a separate promise
chain -- could use the same chain with `await`. */
response
.clone()
.json()
.then(body => console.log("intercepted response:", body))
.catch(err => console.error(err))
;
/* the original response can be resolved unmodified: */
//return response;
/* or mock the response: */
return {
ok: true,
status: 200,
json: async () => ({
userId: 1,
id: 1,
title: "Mocked!!",
completed: false
})
};
};
// test it out with a typical fetch call
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then(response => response.json())
.then(json => console.log("original caller received:", json))
.catch(err => console.error(err))
;
For intercepting the fetch request and parameter we can go for below mentioned way. its resolved my issue.
const constantMock = window.fetch;
window.fetch = function() {
// Get the parameter in arguments
// Intercept the parameter here
return constantMock.apply(this, arguments)
}
For intercepting the response body you need to create a new Promise and resolve or reject current into "then" code. It solved for me and keep content for real app, e.g. React, etc.
const constantMock = window.fetch;
window.fetch = function() {
console.log(arguments);
return new Promise((resolve, reject) => {
constantMock
.apply(this, arguments)
.then((response) => {
if (response.url.indexOf("/me") > -1 &&
response.type != "cors") {
console.log(response);
// do something for specificconditions
}
resolve(response);
})
.catch((error) => {
reject(response);
})
});
}
const fetch = window.fetch;
window.fetch = (...args) => (async(args) => {
var result = await fetch(...args);
console.log(result); // intercept response here
return result;
})(args);
Further to Hariharan's answer, Here is how I updated spinner state in Redux before and after each fetch request
import store from './../store';
// Set up interceptor on all fetch API calls
// Increments redux spinner state when api is called
// Decrements redux spinner state again when it is returned
(function() {
const originalFetch = window.fetch;
window.fetch = function() {
store.dispatch({type: 'show-spinner'})
return originalFetch.apply(this, arguments)
.then((res) => {
store.dispatch({type: 'hide-spinner'})
return res;
})
}
})();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With