Apollo client onError not retrying request

Viewed 1215

So according to the apollo docs for apollo-link-error, onError can be used to handle re-authentication if used with forward(operation).

So I wrote the following code

import { ApolloClient } from 'apollo-client'
import { createHttpLink, HttpLink } from 'apollo-link-http'
import { setContext } from 'apollo-link-context'
import { InMemoryCache } from 'apollo-cache-inmemory'
import AsyncStorage from '@react-native-community/async-storage'
import { refresh } from 'react-native-app-auth'
import { onError } from 'apollo-link-error'
import { ApolloLink, from } from 'apollo-link'
import { RetryLink } from "apollo-link-retry"
import { KC_CONFIG } from '../../config/env'

const httpLink = new HttpLink({
  uri: 'graphqlEndpointOfYourchoice'
})

const authLink = setContext(async (_, { headers }) => {
  const accessToken = await AsyncStorage.getItem('accessToken')
  const unwrappedAccessToken = JSON.parse(accessToken)

  return {
    headers: {
      ...headers,
      authorization: unwrappedAccessToken ? `Bearer ${unwrappedAccessToken}` : "",
    }
  }
})

const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
  if (graphQLErrors) {
    AsyncStorage.getItem('refreshToken')
      .then(data => {
        const refreshToken = JSON.parse(data)
        // console.log(data)

        refresh(KC_CONFIG, {
          refreshToken,
        })
          .then(({ accessToken, refreshToken }) => {
            const oldHeaders = operation.getContext().headers
            operation.setContext({
              ...oldHeaders,
              authorization: accessToken
            })
            console.log(oldHeaders.authorization)
            console.log(accessToken)
            // console.log(refreshToken)

            AsyncStorage
              .multiSet([
                ['accessToken', JSON.stringify(accessToken)],
                ['refreshToken', JSON.stringify(refreshToken)]
              ])
            // tried putting forward() here <--------------
          })
          .catch(e => {
            if (e.message === 'Token is not active') console.log('logging out')
            else console.log('Refresh error: ' + e)
          })
      })
      .then(() => {
        console.log('Refreshed the accesstoken')
        return forward(operation)
      })
      .catch(e => {
        console.log('Storage error: ' + e)
      })
  }

  if (networkError) {
    console.log('network error: ' + networkError)
  }
  // tried putting forward() here <--------------
})

const retryLink = new RetryLink()

export const client = new ApolloClient({
  link: from([
    retryLink,
    errorLink,
    authLink,
    httpLink
  ]),
  cache: new InMemoryCache()
})

This does not achieve the desired result. The error gets caught and runs it's course, refreshing the token as it should, but it never does a second request.

1 Answers

try this:

export const logoutLink = onError(({ networkError, operation, forward }) => {
      if (networkError?.statusCode === 401) {
        return new Observable(observer => {
          (async () => {
            try {
      const newToken = await getToken();

      // Modify the operation context with a new token
      const oldHeaders = operation.getContext().headers;

      operation.setContext({
        headers: {
          ...oldHeaders,
          authorization: `Bearer ${newToken}`,
        },
      });

      const subscriber = {
        next: observer.next.bind(observer),
        error: observer.error.bind(observer),
        complete: observer.complete.bind(observer),
      };

      // Retry last failed request
      forward(operation).subscribe(subscriber);
    } catch (error) {
      observer.error(error);
    }
  })();
});
  }
});
Related