I have a fetch block which integrates to an API and sets the bearer token in the headers. Sometimes when calling the API the bearer token is expired, in this scenario I need to handle the 401 response in catch block by calling a refresh endpoint, and set the access token value in a cookie. After which I can attempt to call the API again by using the new token set in the cookie.
I have the following fetch block
fetch(host + '/api/v1/list', {
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer " + getBearerToken()
}
}).then((response) => {
return response.json();
}).then((data) => {
return data.items;
}).catch(response => {
return fetch(host + 'refresh', {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer " + getBearerToken()
}
});
}).then((response) => {
return response.json();
}).then((data) => {
let token = data['Access_token'];
document.cookie = 'SSO_token=' + token + '; expires=0; path=/';
return fetch(host + '/api/v1/list', {
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer " + getBearerToken()
}
});
}).then((response) => {
return response.json();
}).then((data) => {
return data.items;
})
The console reports a TypeError in the first call to then after the catch
}).catch(response => {
return fetch(host + 'refresh', {
method: 'POST',
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer " + getBearerToken()
}
});
}).then((response) => {
return response.json();
})
response.json is not a function
. I’m not understanding the error here. Any help appreciated
3
Answers
I would suggest to change the sequence of
catch
andthen
which is in the 2nd code block…The sequence of catch and then matters,
According to on stackOverflow answer,
Change to this,
Hope it helps
The immediate issue is the following sequence
(shortened for brevity)
This would only work if the first
fetch()
fails which would then jump over the firstresponse.json()
, go to the.catch()
and then continue with the nextresponse.json()
.However, if the first
fetch()
succeeds, then the.catch()
would be passed over, as there is no error. This leads to effectively the following code:Taking a step back, the problem that led to this one is trying to do so many operations in the same sequence of promises. Much easier is to define what needs to be done. Something like
Which can then more easily be composed together and would be more readable and easier to make sure it is correct:
With this code the token refresh is only attempted if the request fails and only once. Moreover, the refresh is itself in a separate function, so it does not need to be handled specially in the promise chain. Including it anywhere will mean the entire refresh process is done before continuing, without needing to try and handle the response of the refresh in the same promise chain as handling the other responses.
The second request can still fail two times in a row. Presumably this would not be due to the access token but for some other reason. At any rate, it is advised to handle other possible failures appropriately.
The second
.then(response => …)
is called with either the result of the.catch()
callback (i.e. the refetch response) or – if there was no error in the first place – with the result of the previousthen
callback (i.e. thedata.items
), and the latter value has no.json()
method.You want to run all of that code only if an error occurred, so you need to nest that entire promise chain inside the
catch
callback:However, the
catch
is only handling network errors and json parsing errors, but not the http errors that you actually want to handle by retrying the request with a new token. To achieve that, you need to explicitly check the status of the response: