Maria Raiven
Maria Raiven

Reputation: 65

Apollo Graphql when refreshing token doesn't update useQuery

I'm facing next problem: I have a useQuery hook for getting usersData

GET_USER_INFO useQuery

const { data:dataGetUserInfo, loading:loadingQuery, error:errorQuery,refetch  } = useQuery(GET_USER_INFO, {variables: {
            getUserInfoInfoId: userId
        }});

And I want to refresh access and refresh tokens when access token is expired. For that I've done errorLink in apollo-client.ts which calls a refesh fucntion when it needs to and then recall my GET_USER_INFO useQuery

apollo-client.ts

const getNewToken =  () => {
    let refreshToken

        refreshToken = localStorage.getItem("REFRESH");

    return client.mutate({
        mutation: REFRESH_TOKEN,
        variables:{
            refreshTokenRefreshToken : refreshToken,
        }
    })
        .then((res) => {
            localStorage.clear();
            const { accessToken,refreshToken } = res.data.refreshToken;
            localStorage.setItem('REFRESH',refreshToken)
            localStorage.setItem('AUTHORIZATION',accessToken)
            return res.data;
        }).catch((err)=>{
            console.log(err)
            
        })

};



const errorLink = onError(
    ({ graphQLErrors, networkError, operation, forward }) => {
        if (graphQLErrors) {
            for (let err of graphQLErrors) {

                switch (err.extensions?.code) {
                    
                    case "UNAUTHENTICATED" :
                        console.log('aa')
                        return fromPromise(
                            getNewToken().catch((error) => {
                                localStorage.clear();
                                // Handle token refresh errors e.g clear stored tokens, redirect to login
                                return;
                            })
                        )
                            .filter((value) => Boolean(value))
                            .flatMap(({accessToken, refreshToken}) => {

                                const oldHeaders = operation.getContext().headers;
                                // modify the operation context with a new token
                                operation.setContext({
                                    headers: {
                                        ...oldHeaders,
                                        authorization: `Bearer ${accessToken}`,
                                    },
                                });

                                // retry the request, returning the new observable
                                return forward(operation);
                            });
                   

                }
            }
        }
    }
);



const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      authorization: localStorage.getItem("AUTHORIZATION") ? `Bearer ${localStorage.getItem("AUTHORIZATION")}` : "",
    },
  };
});




const client = new ApolloClient({
  link: ApolloLink.from([errorLink,authLink, httpLink]),
})

The problem is: it doesn't refetch data in my useQuery after successfull refreshing tokens

2 query

4 query

btw it does one more failing request i don't know why

3 query

Upvotes: 2

Views: 1906

Answers (1)

Herku
Herku

Reputation: 7666

You can use the retry link to run the query again.

You should chain your links in a way that the retry link is the most outer link so that you can wait for your error link to fetch the token. Check out the custom strategies, here you could react to a specific status code in the error object or listen to a custom property, that the error link could attach to the operation object. This way you can even prevent a refetch if the token refresh failed (e.g. because the refetch token also timed out).

The links are also all open source so if chaining them doesn't really work for you, you could also develop your own refetch+retry link by combining the code of both links.

Upvotes: 1

Related Questions