mlegenhausen / fetch-intercept Goto Github PK
View Code? Open in Web Editor NEWInterceptor library for the native fetch command inspired by angular http intercepts.
License: MIT License
Interceptor library for the native fetch command inspired by angular http intercepts.
License: MIT License
When I am trying to get Response body using Body mixin json() method
response: function (response) {
response.json().then((data) => {
alert(JSON.stringify(data))
});
return response;
}
I am able to see resonse body data in the alert box but I am getting error:
TypeError: Already read
at consumed (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:17774:31)
at Response.Body.text (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:17892:24)
at Response.Body.json (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:17916:21)
at response (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:1581:17)
at tryCallOne (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:18312:14)
at blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:18413:17
at blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:5214:21
at _callTimer (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:5103:9)
at _callImmediatesPass (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:5139:9)
at Object.callImmediates (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:5358:14)": TypeError: Already read
at consumed (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:17774:31)
at Response.Body.text (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:17892:24)
at Response.Body.json (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:17916:21)
at symbolicateStackTrace$ (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:17553:54)
at tryCatch (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:18983:19)
at Generator.invoke [as _invoke] (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:19156:24)
at Generator.prototype.(anonymous function) [as next] (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:19026:23)
at tryCatch (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:18983:19)
at invoke (blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:19059:22)
at blob:http://localhost:8081/10a78587-5550-4332-a55f-9e31c45f9f4f:19069:15
Due to this error I not able to navigate to next screen.
I am using Mobx for State management.
Hi. It doesn't look like it's possible to swap out the fetch
implementation currently. Is it? If not, will it be? As it stands, we're using isomorphic-fetch throughout our codebase and the interceptors from this lib aren't picking up the network traffic.
This happens when I specify the requestError
function. Tested it in a new create-react-app with https://github.com/github/fetch.
import fetchIntercept from 'fetch-intercept';
const unregister = fetchIntercept.register({
requestError: (error) => {
return Promise.reject(error);
},
responseError: (error) => {
// catches bad error here
return Promise.reject(error);
},
});
fetch('http://google.com');
// Unregister your interceptor
unregister();
Looking at the source, _ref.request
is undefined.
reversedInterceptors.forEach(function (_ref) {
var request = _ref.request; // request is undefined
var requestError = _ref.requestError;
if (request || requestError) {
promise = promise.then(function (args) {
return request.apply(undefined, _toConsumableArray(args));
}, requestError);
}
});
I have registered an interceptor in my app.js file in react as follows:
fetchIntercept.register({
response: function (response) {
if (response.status >= 500 && response.status < 600) {
bottomToast("server error")
}
return response;
}
});
However, when I use SWR to fetch data, I encounter a CORS error. Here's the code I'm using for the fetch:
const fetcher = (...args) => fetch(...args, { headers:{ accessToken: state.accessToken } }).then((res) => res.json())
const {data: profile} = useSWR(state.isLogin ? `${portalAddress}/user/${state.userId}` : null, fetcher);
It seems that the OPTIONS request is being sent before the fetch request, which is causing the CORS error.
It's important to note that this issue only occurs in production mode and not in the development environment. The error message I receive is shown in the attached screenshot.
By default, fetch won't send or receive any cookies from the server, resulting in unauthenticated requests. In order to include cookies, we have to add "credentials:include" for each REST API requests which are implemented in Fetch API. But it is not maintainable if we manually add such an option everywhere when Fetch is used. So we decided to look for help from fetch-intercept. Here is our implementation:
fetchIntercept.register({ request: function (url, config) { config.credentials = 'include'; return [url, config]; }, ... };
But after the change, all Fetch functions stop working and the following exceptions are found in Chrome developer console:
auth-window-actions.js:81 Uncaught (in promise) Error: Failed to fetch login user info: TypeError: Cannot set property 'credentials' of undefined.
Any suggestion to it? Our front end is implemented by React v16.3.
Hi, the question is not complicated. How to use correctly your library with React?
I make a function call in this way:
import fetchIntercept from './fetchIntercept';
const App = () => {
fetchIntercept(); <---
return (
<>
...content...
</>
);
};
any solutions ?
Hi,
I am trying to make a common interceptor to authorize all my outgoing requests.
My logic is as follows:
All my outgoing requests/API calls passes through the interceptor first to check on my token expiry and in-case the response was un-authorized I make an internal call to refresh the token and update my storage keys with the new values.
Now I need to recall the original request with the new token value, but I can't figure out how to detect the original request that passed through the interceptor.
This is my code :
export const unregister = fetchIntercept.register({
request: function (url, config) {
return [url, config];
},
requestError: function (error) {
return Promise.reject(error);
},
responseError: function (error) {
return Promise.reject(error);
},
response: function (response) {
if (response.status == 401) {
Services.refreshToken((res)=>{
if (res.message == 'success') {
// if token has been refreshed
// recall the request again
}else {
// login again
}
})
}else {
return response;
}
}
})
The problem is that I've no idea how to recall the same request that passed through the interceptor.. I did some search on this, but couldn't find a way to execute this.
Greetings! First of all, thank you for all your effort on the library, works great ๐
I am using version: 2.4.0.
I am using the interceptors to measure response time, so wanted to know a way I could pass in some kind of object as state between the request and the response functions. I am looking for something like this:
fetchIntercept.register({
request: function (url, config) {
return [url, config, {state: 'Good!'}]
},
response: async function (response, state) {
console.log(state) // logs {state: "Good!"}
return response
}
})
My request comes that if I store the state inside a file, I'll get the values overriden when different respond at different times.
Any way to walk this around? It could also work to access the state from within the response object, as we do with the request object.
Thanks!
This might be an issue with the lack of documentation. I am not able to add headers.
in Jest test
import fetch from 'node-fetch'
global.fetch= fetch
import fetchIntercept from "fetch-intercept";
throws
โ Test suite failed to run
No fetch avaibale. Unable to register fetch-intercept 10 | 11 | // fetchIntercept.register(errorInterceptor) > 12 | // fetchIntercept.register(dataInterceptor) | ^ 13 | // fetchIntercept.register(cookieInterceptor) 14 | // fetchIntercept.register(headersInterceptor) 15 | at attach (node_modules/fetch-intercept/lib/webpack:/src/attach.js?1269:38:13) at Object.<anonymous> (node_modules/fetch-intercept/lib/webpack:/src/node.js:3:18) at Object.<anonymous> (node_modules/fetch-intercept/lib/node.js:53:31) at __webpack_require__ (node_modules/fetch-intercept/lib/webpack:/webpack/bootstrap 288f28a76d94cd56de0b?2753:19:1) at node_modules/fetch-intercept/lib/webpack:/webpack/bootstrap 288f28a76d94cd56de0b?2753:39:1 at Object.<anonymous> (node_modules/fetch-intercept/lib/node.js:44:10) at Object.<anonymous> (src/api/index.js:12:46) at Object.<anonymous> (src/plugins/bottle.js:24:35) at Object.<anonymous> (src/models/AbstractSync.js:25:15) at Object.<anonymous> (src/models/index.js:27:44) at Object.<anonymous> (src/plugins/className.js:5:38) at Object.<anonymous> (tests/test-setup.js:30:1)
My scenario is as follows:
The question is as follows: would it be possible to return a Promise from the 'request' event instead of a fixed array with the [url, config]? The token refresh call is made using a promise, and I'd like to inject the auth header in the .then of the promise. The 'request' does not wait for the promise resolve. Example code of what' I'm trying to do:
this.unregisterFetchInterceptor = fetchInterceptor.register({
request: (url, config) => {
this.refreshAuthTokenIfExpired(resourceName).then(() => {
let modifiedConfig = this.injectAuthHeader(resourceName, config);
return [url, modifiedConfig];
});
}
});
Thanks,
Adam
This is a question. How would I use the "response" interceptor to modify or transform the data?
Similar logic to how the axios interceptor works. So for any 400 errors, we can refresh the token and send back the original request.
But without access to the original request and request body, this isn't possible.
There seems to be PR #36 open which addresses this, but it hasn't been merged.
Any update if / when this might get done?
Type '(response: FetchInterceptorResponse) => Promise' is not assignable to type '(response: FetchInterceptorResponse) => FetchInterceptorResponse'.
export interface FetchInterceptor {
request?(url: string, config: any): Promise<any[]> | any[];
requestError?(error: any): Promise;
response?(response: FetchInterceptorResponse): FetchInterceptorResponse;
responseError?(error: any): Promise;
}
So, the example of refresh token is not usable with ts...
Hi!
Is this still maintained? There are ongoing issues that could be easily fixed, and it hasn't been for months.
Thanks,
I have the following code:
...
import fetchIntercept from 'fetch-intercept';
class UserActions {
async logout() {
const unregister = fetchIntercept.register({
request: function (url, config) {
// Modify the url or config here
console.log("LOGOUT INTERCEPT", url, config);
return [url, config];
},
requestError: function (error) {
// Called when an error occured during another 'request' interceptor call
return Promise.reject(error);
},
response: function (response) {
// Modify the reponse object
console.log("LOGOUT INTERCEPT", response);
return response;
},
responseError: function (error) {
// Handle an fetch error
return Promise.reject(error);
}
});
let err, response, stat;
try {
response = await fetch(`${apiUrl}/user/logout`);
if (response.ok) {
Expectation: Requests are intercepted and logged in the console
Result: Nothing gets logged in the console.
I'm trying to use this as part of a webpack-based web UI. I installed it with npm install --save-dev fetch-intercept
. My code looks like:
import fetchIntercept from 'fetch-intercept';
fetchIntercept.register({
request: (url, config) => [url, config],
requestError: err => Promise.reject(err),
response: res => res,
responseError: err => Promise.reject(err)
});
in authFetchIntercept.js
and I have import './authFetchIntercept.js'
inside my javascript's entrypoint. Trying to build the site with webpack results in:
ERROR in ./~/fetch-intercept/lib/index.js
Module not found: Error: Cannot resolve module 'whatwg-fetch' in /Users/jeffreycharles/projects/facial-recognition-webui/node_modules/fetch-intercept/lib
@ ./~/fetch-intercept/lib/index.js 261:18-41
When I use this package in client with whatwg-fetch
works perfect, but if I use in server with node-fetch
config is undefined
.
global['fetch'] = require('node-fetch');
fetchIntercept = require('fetch-intercept');
fetchIntercept.register({
request: function (url, config) {
console.log(config);
console.log('request server');
return [url, config];
},
});
Probably I'm doing something wrong here, but cannot find.
Why I need config? Because I want to modify headers, I did that but still not adding headers:
const config = defaultConfig || {};
config.headers = config.headers || {};
config.headers['language-filter'] = true;
config.headers['Accept-Language'] = 'en';
return [url, config];
Hi,
Of course I prefer to use webpack as a bundler (I would highly recommend it)
But trouble is that I cannot get access to the index.js source file to use it in debug mode (without webpack-dev-server, with just some naive configuration)
Would you like to withdraw index.js file from .npmignore for me ?
(I know why you did that, I did the same once, I removed src so that people had to consider the module as a black box)
Thank you for your time,
LiBe.
Are there any TypeScript definitions available anywhere for this module? Would really appreciate to be able to use this in my TypeScript environment.
Keep up the good work! ๐
There is getFirstObjectKey function in the source, but isn't it going to fail sometimes since order of the object keys is not guaranteed?
I'm writing a hook on fetch response. I need both the URL and the method of the request to decide whether the hook should be called or not.
However, in the response hook, I can only get URL. Do you know where could I get the method('post', 'get', 'put', etc.) that the request is using in the response hook?
fetchIntercept.register({
response: function(response) {
if(response.url === predefinedURL && response.method /* I don't know how to get it */ === predefinedMethod) {
//do something here
}
}
});
Many Thanks
Why?
Hi, I am using typescript with React Native and node. I need to add an interceptor to check for reachability by verifying whether the device has a network connection. How do I cause a request to fail from within the 'request' block. I have tried throwing an error but I get an 'unhanded promise rejection' error instead.
Here is my code:
//register the new interceptor
ReachabilityFetchInterceptor._unregisterInterceptor = registerFetchIntercept({
request: async (url: string, options: any): Promise<Array<string>> => {
//Check whether the url matches the regexp
//check whether react native thinks we're offline
NetInfo.getConnectionInfo().then((connectionInfo) => {
if (connectionInfo.type === "NONE" || connectionInfo.type === "UNKNOWN") {
throw new Error("Reachability Error - Device offline");
}
}).catch((err) => {
return Promise.reject(err);
});
},
requestError: (error: any): Promise<void> => {
//do something here after the error is caught
},
...
i'd love some advice on how to proceed with this?
thanks!
My scenario is this: on a single page I have multiple independent components that use the fetch API. All of them require to mutate requests (add request headers, etc.) and handle the response, so they all register an interceptor. The issue here is that a fetch request from one component is intercepted by all other components interceptors. Does anyone have an idea how to ensure that a fetch request originating from one component is intercepted only by an interceptor registered by the same component? In older versions of JS there was something like arguments.caller, so it was possible to identify where the function call originated, but this is deprecated now and not available in "use strict" mode.
Hello! There is very popular mock library fetch-mock https://github.com/wheresrhys/fetch-mock which is not work togetger with fetch-intercept.
Is it possible to setup them both to work together?
I have a requirement to get a JWT from a service using an async request, and I'll need that token to set as a header in the 'request' part of the fetch-intercept pipeline here. However, since request is sync, I cannot set the token correctly unless I wait for the token to come back before running any requests. I am already able to set the token as I'm describing using an HttpInterceptor in Angular since the intercept() method they implement has the option to be a Promise.
If you could do the same, just like you already have with requestFailure and the other methods, then that would make your library a magnitude more powerful.
I currently have this in my auth.tsx
file, and it works fine
import fetchIntercept from 'fetch-intercept';
export default fetchIntercept.register({
request: function (url, config) {
// Modify the url or config here
config.headers.append = {
'content-type': 'application/json',
'Authorization': 'my authorization'
}
return [url, config];
},
requestError: function (error) {
// Called when an error occured during another 'request' interceptor call
return Promise.reject(error);
},
response: function (response) {
// Modify the reponse object
return response;
},
responseError: function (error) {
// Handle an fetch error
return Promise.reject(error);
}
});
export function login(username: string, password: string) {
// ${process.env.REACT_APP_API_BASE}/api/v1/users/login/
return fetch('http://localhost:8000/api/v1/users/login/', {
method: 'POST',
body: JSON.stringify({ 'username': username, 'password': password })
})
}
Let's say I have books.service.tsx
file, and I wanna make use of the already defined interceptor from the auth.tsx
in there. How do I go about it?
Request intercept Interface looks like this::
request?(url: string, config: any): Promise<any[]> | any[];
Based on looking at the code , the library invoked intercept's request api with the exact same args as that of fetch
so it should be
request?(input: RequestInfo, init?: RequestInit): Promise<any[]> | any[];
This will make the library usage better
Hey @mlegenhausen ๐ Great library and amazing work.
I'm trying to read the response.request.body
in the response
interception:
response: function (response) {
response.text().then((value) => {
console.log('Response here')
console.log(value)
})
const requestClone = response.request.clone()
requestClone.text().then((value) => {
console.log('Request body here')
console.log(value)
})
return response;
},
I'm getting Uncaught (in promise) TypeError: Failed to execute 'clone' on 'Request': Request body is already used
. I have tried with:
response.request.text().then((value) => {
console.log('Request body here')
console.log(value)
})
But I'm getting TypeError: Failed to execute 'clone' on 'Request': Request body is already used
.
My use case is to whenever there's a response I would like to log the request body and the response body. Is it possible to do it entirely in the response
or do I have to keep track of the request, and whenever I get a response I look for the request and merge them?
Hi guys,
I had a requirement where I want to intercept the response, when the token is expired using refresh token.
response: async function (response) {
if(response.status === 401)
{
//call the refreshToken api
// update token
return fetch(url, config) //call fetch again with new token
}
else
{
return response
}
},
In the last release of the library, there was an update about request data passed with the response through which we can get the url. But how can I get the config details too. As there is no proper example or documentation on this, can anyone help me out here.
In the following open issue #39 , there is code provided but the implementation
wont work, in a case where there are multiple request been sent in same time.
Thanks,
:)
I didnt want to comment on issue #16 but i see the same issue. i really just care about intercepting a 401 but the response console.log is never hit. even if its the only thing in there. responseError does tho.
fetchIntercept.register({
responseError: function(error) {
console.log('responseError', error)
}
});
fetchIntercept.register({
response: function(response) {
console.log('fetchIntercept response', response)
if (response.status >= 401) throw new Error('Request error');
return response;
}
});
// Call fetch to see your interceptors in action.
fetch('http://google.com');
When I use the new Promise option for 'request' I get this error
Short
TypeError: Cannot read property '_instance' of undefined
Stacktrace location
TypeError: Cannot read property '_instance' of undefined
at register (index.js:130)
index.js line 130
/**
* Register intercept hooks & return an interceptor instance
* @param {object} hooks - The intercept hooks
* @return {FetchInterceptor} An interceptor object
*/
value: function register() {
var hooks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
if (this._instance) { // <<<<----- HERE IS LINE 130 of index.js
return this._instance;
}
var interceptor = new this();
for (var i = 0; i < this.hooks.length; i++) {
var hook = this.hooks[i];
if (typeof hooks[hook] === 'function') {
interceptor[hook] = hooks[hook];
}
}
interceptor.hijack();
this._instance = interceptor;
return interceptor;
}
Here is how I setup my fetchInterceptor register call
fetchInterceptor.register({
request: (url, config) => {
return this.claimsService.getTokenForUser()
.then(token => {
config.headers['Authorization'] = token ? `Bearer: ${token}` : null;
return [url, config];
});
},
requestError: function (error) {
return Promise.reject(error);
},
response: function (response) {
return response;
},
responseError: function (error) {
return Promise.reject(error);
}
});
The getTokenForUser() call is a Promise.
Maybe the switch to use Promise on the request method needs to take a closer look?
When using webpack fetch-intercept
things it is in a node environment cause require
and process
exists when building with webpack. The intended fix to prioritise the web environment.
If you can make an automatic unregister interceptor, it will be perfect.
Hi, Is it possible to perform asynchronous calls in a "request" interceptor? I have an interceptor that gets the access token from the localforage library but all methods are asynchronous. If so could you provide a code example please? Thanks
Hey, I was wondering if there are any recommended ways to unit test fetch interceptors. I have tried using fetch-mock to mock a http request however that package doesn't allow the fetch request to hit the interceptor.
Does anyone have any experience testing fetch interceptors, specifically using mocha tests
Thanks!
Fetch -intercept depends on whatwg-fetch, but in dependencies there is no whatwg-fetch, and this will be an error if the user does not have whatwg-fetch locally
"* whatwg-fetch in ./node_modules/fetch-intercept/lib/browser.js
To install it, you can run: npm install --save whatwg-fetch"
Meanwhile, I hope the author can try to upgrade the whatwg-fetch
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.