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
you can use "?" before period "." and use
typeof "something" !== "undefined"
in your code, you can do
request?.resolve(accessToken)
The problem is that
resolve
does not return a promise. It returnsundefined
.Taking a step back, you should not need a function like
addRequestToQueue
that creates a new promise and pushes itsresolve
function in a queue. To create anew 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 thethen
call. Like this: