skip to Main Content

I want to refresh token in the background in axios "axios": "^1.3.4",, when the server side return access token expire, the client store the request in the queue and restore the request after the new token refreshed. when I using promise then in react like this:

instance.interceptors.response.use((response:AxiosResponse<any, any>) => {
  const originalRequest:InternalAxiosRequestConfig<any> = response.config;
  if(isRefreshing){
    addRequestToQueue(originalRequest);
  }
  if (!isRefreshing) {
    if(response.data.resultCode === ResponseCode.ACCESS_TOKEN_EXPIRED){
      addRequestToQueue(originalRequest);
      isRefreshing = true;
      // refresh the access token
      ResponseHandler.handleWebCommonFailure(response.data)
      .then((data:any) => {
        isRefreshing = false;
        pendingRequestsQueue.forEach((request) => {
          const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
          request.resolve(accessToken)
          .then((resp:any)=>{
            // get the action of original request
            const action = request.action;
            const data = resp.data.result;
            // change the state to make it render the UI
            store.dispatch(action(data));
          });
        });
        pendingRequestsQueue = [];
      });
    }
  }
  return response;
},
  (error: any) => { return Promise.reject(error) }
)

this line request.resolve(accessToken) shows error caught (in promise) TypeError: Cannot read properties of undefined (reading 'then'), this is the addRequestToQueue function define:

function addRequestToQueue(originalRequest: any){
  return new Promise((resolve, reject) => {
    pendingRequestsQueue.push({ resolve, reject });
  })
    .then((data:any) => {
      originalRequest.headers['x-access-token'] = data.accessToken;
      originalRequest.headers['x-request-id'] = uuid();
      return instance(originalRequest);
    })
    .catch((err) => {
      return Promise.reject(err);
    });
}

what should I do to fixed this issue? This is my full code of axios client:

import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import { v4 as uuid } from 'uuid';
import store from '../store/store';
import { ResponseCode, ResponseHandler, WheelGlobal } from 'js-wheel';

let isRefreshing = false
let pendingRequestsQueue: Array<any> = [];

const instance = axios.create({
  timeout: 60000
})

instance.defaults.headers.post['Content-Type'] = 'application/json'

instance.interceptors.request.use((request) => {
  const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
  accessToken && (request.headers['x-access-token'] = accessToken);
  request.headers['x-request-id'] = uuid();
  return request
},
  (error: any) => {
    return Promise.reject(error)
  }
)

function addRequestToQueue(originalRequest: any): Promise<any> {
  return new Promise((resolve, reject) => {
    pendingRequestsQueue.push({ resolve, reject });
  })
    .then((data: any) => {
      originalRequest.headers['x-access-token'] = data.accessToken;
      originalRequest.headers['x-request-id'] = uuid();
      return instance(originalRequest);
    })
    .catch((err) => {
      return Promise.reject(err);
    });
}

instance.interceptors.response.use((response: AxiosResponse<any, any>) => {
  const originalRequest: InternalAxiosRequestConfig<any> = response.config;
  if (isRefreshing) {
    addRequestToQueue(originalRequest);
  }
  if (!isRefreshing) {
    if (response.data.resultCode === ResponseCode.ACCESS_TOKEN_EXPIRED) {
      addRequestToQueue(originalRequest);
      isRefreshing = true;
      // refresh the access token
      ResponseHandler.handleWebCommonFailure(response.data)
        .then((data: any) => {
          isRefreshing = false;
          pendingRequestsQueue.forEach((request) => {
            const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
            const promise = request.resolve(accessToken);
            if (promise) {
              promise.then((resp: any) => {
                // get the action of original request
                const action = request.action;
                const data = resp.data.result;
                // change the state to make it render the UI
                store.dispatch(action(data));
              });
            }
          });
          pendingRequestsQueue = [];
        });
    }
  }
  return response;
},
  (error: any) => { return Promise.reject(error) }
)

export function requestWithAction(config: any, action: (arg0: any) => any) {
  return instance(config).then(
    (response: { data: { result: any; }; }) => {
      const data = response.data.result;
      store.dispatch(action(data));
      return response.data;
    }
  ).catch(
    (error: any) => {
      console.error(error);
    }
  );
}

2

Answers


  1. you can use "?" before period "." and use typeof "something" !== "undefined"
    in your code, you can do request?.resolve(accessToken)

    Login or Signup to reply.
  2. The problem is that resolve does not return a promise. It returns undefined.

    Taking a step back, you should not need a function like addRequestToQueue that creates a new promise and pushes its resolve function in a queue. To create a new Promise when you already have a promise to use (instance()) is an anti-pattern.

    Instead, put the request in the queue and call the axios (instance) in your main function upon which you can chain the then call. Like this:

    instance.interceptors.response.use((response:AxiosResponse<any, any>) => {
      const originalRequest:InternalAxiosRequestConfig<any> = response.config;
      if(isRefreshing){
        pendingRequestsQueue.push(originalRequest); // <--
      }
      if (!isRefreshing) {
        if(response.data.resultCode === ResponseCode.ACCESS_TOKEN_EXPIRED){
          pendingRequestsQueue.push(originalRequest); // <--
          isRefreshing = true;
          // refresh the access token
          ResponseHandler.handleWebCommonFailure(response.data)
          .then((data:any) => {
            isRefreshing = false;
            pendingRequestsQueue.forEach((request) => {
              const accessToken = localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME);
              // Moved code from `addRequestsToQueue` here:
              request.headers['x-access-token'] = accessToken;
              request.headers['x-request-id'] = uuid();
              instance(request) // This returns a promise (contrary to `resolve`)
              .then((resp:any)=>{
                // get the action of original request
                const action = request.action;
                const data = resp.data.result;
                // change the state to make it render the UI
                store.dispatch(action(data));
              });
            });
            pendingRequestsQueue = [];
          });
        }
      }
      return response;
    },
      (error: any) => { return Promise.reject(error) }
    )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search