Apollo link offers an error handler onError
Issue:
Currently, we wish to refresh oauth tokens when they expires during an apollo call and we are unable to execute an async fetch request inside the onError
properly.
Code:
initApolloClient.js
import { ApolloClient } from 'apollo-client';
import { onError } from 'apollo-link-error';
import { ApolloLink, fromPromise } from 'apollo-link';
//Define Http link
const httpLink = new createHttpLink({
uri: '/my-graphql-endpoint',
credentials: 'include'
});
//Add on error handler for apollo link
return new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
//User access token has expired
if(graphQLErrors[0].message==="Unauthorized") {
//We assume we have both tokens needed to run the async request
if(refreshToken && clientToken) {
//let's refresh token through async request
return fromPromise(
authAPI.requestRefreshToken(refreshToken,clientToken)
.then((refreshResponse) => {
let headers = {
//readd old headers
...operation.getContext().headers,
//switch out old access token for new one
authorization: `Bearer ${refreshResponse.access_token}`,
};
operation.setContext({
headers
});
//Retry last failed request
return forward(operation);
})
.catch(function (error) {
//No refresh or client token available, we force user to login
return error;
})
)
}
}
}
}
}
}),
What happens is:
- Initial graphQL query runs and fails due to unauthorization
- The
onError
function ofApolloLink
is executed. - The promise to refresh the token is executed.
- The
onError
function ofApolloLink
is executed again?? - The promise to refresh the token is completed.
- The initial graphQL query result is returned and its data is
undefined
Between step 5 and 6, apollo doesn't re-run the initial failed graphQL query and hence the result is undefined
.
Errors from console:
Uncaught (in promise) Error: Network error: Error writing result to store for query:
query UserProfile($id: ID!) {
UserProfile(id: $id) {
id
email
first_name
last_name
}
__typename
}
}
The solution should allow us to:
- Run an async request when an operation fails
- Wait for the result of the request
- Retry failed operation with data from the request's result
- Operation should succeed to return its intended result
I'm refreshing the token this way (this is updated your code):