Axios multiple request on interceptor

Viewed 6833

I'm using the library axios in my react app.
I'm having a problem with the interceptor.

My question is let say I have three requests happening concurrently and I don't have the token, the interceptor calling the getUserRandomToken three time, I want the interceptor will wait until I'm getting the token from the first request and then continue to the others.

P.S. the token he is with an expiration date so I also checking for it and if the expiration date is not valid I need to create a new token.

This is the interceptor:

axios.interceptors.request.use(
  config => {
    /*I'm getting the token from the local storage
    If there is any add it to the header for each request*/
    if (tokenExist()) {
      config.headers.common["token"] = "...";
      return config;
    }
    /*If there is no token i need to generate it
     every time create a random token, this is a axios get request*/
    getUserRandomToken()
      .then(res => {
        /*add the token to the header*/
        config.headers.common["token"] = res;
        return config;
      })
      .catch(err => {
        console.log(err);
      });
  },
  function(error) {
    // Do something with request error
    return Promise.reject(error);
  }
);

3 Answers

How about singleton object that will handle the token generations? something similar to this:

const tokenGenerator ={
  getTokenPromise: null,
  token: null,
  getToken(){
    if (!this.getTokenPromise){
      this.getTokenPromise = new Promise(resolve=>{
        /*supposed to be a http request*/
        if (!this.token){
          setTimeout(()=>{
            this.token = 'generated';
            resolve(this.token);
          },0)
        }else{
          resolve(this.token);
        }
      })
    }
    return this.getTokenPromise;
  }

you can reference this same object from the interceptors.

see example: JS FIddle reference: reference

You can return a Promise from interceptor callback to "wait" until promise fullfiles (this will fit your case). Check out this example:

function axiosCall () {
  return new Promise((resolve, reject) => {
    Axios.post(URL, {apiKey}).then((response) => {
      resolve(response.data.message);
    }).catch((error) => {
      reject(error);
    });
  });
}

instance.interceptors.request.use((config) => {
  return axiosCall().then((tokenResponse) => {
    setWebCreds(tokenResponse);
    config.headers.Authorization = `Bearer ${tokenResponse}`;
    return Promise.resolve(config)
  }).catch(error => {
    // decide what to do if you can't get your token
  })
}, (error) => {
  return Promise.reject(error);
});

More details here: https://github.com/axios/axios/issues/754

Following code doing certain tasks:

  1. Update Token on 401
  2. Make a queue of failed requests while the token is refreshing.
  3. Restore the original request after token refreshing.
  4. Once the peculiar request is given 200, remove it from the queue.

Config.js

import axios from 'axios';
import { AsyncStorage } from 'react-native';
import { stateFunctions } from '../../src/sharedcomponent/static';

const APIKit = axios.create({
    baseURL: '',
    timeout: 10000,
    withCredentials: true,
});
const requestArray = [];
// Interceptor for Request
export const setClientToken = token => {
    APIKit.interceptors.request.use(
        async config => {
            console.log('Interceptor calling');
            let userToken = await AsyncStorage.getItem('userToken');
            userToken = JSON.parse(userToken);
            config.headers = {
                'Authorization': `Bearer ${userToken}`,
                'Accept': 'application/json',
                "Content-Type": "application/json",
                "Cache-Control": "no-cache",
            }
            //   console.log('caling ' , config)
            return config;
        },
        error => {
            Promise.reject(error)
        });
};


// Interceptor for Response
APIKit.interceptors.response.use(
    function (response) {
        if (requestArray.length != 0) {
            requestArray.forEach(function (x, i) {
                if (response.config.url == x.url) {
                    requestArray.splice(i, 1);
                }
            });
        }
        return response;
    },
    function (error) {
        const originalRequest = error.config;
        requestArray.push(originalRequest);
        let reqData = "username=" + number + "&password=" + pin + "&grant_type=password" + "&AppType=2" + "&FcmToken=null";
        // console.log('error ' , error);
        if (error.message === "Request failed with status code 401" || error.statuscode === 401) {
            if (!originalRequest._retry) {
                originalRequest._retry = true;
                return axios({
                    method: 'post',
                    url: '/api/login',
                    data: reqData,
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded",
                        "Cache-Control": "no-cache",
                    }
                })
                    .then(res => {
                        let response = res.data;
                        console.log('successfull Login', response)
                        if (res.data.StatusCode == 200) {
                            AsyncStorage.setItem('userToken', JSON.stringify(response.access_token));
                            stateFunctions.UserId = response.UserId;
                            stateFunctions.CustomerContactID = response.CustomerContactID;
                            let obj = {
                                access_token: response.access_token,
                                token_type: response.token_type,
                                expires_in: response.expires_in,
                                UserId: response.UserId,
                                CustomerContactID: response.CustomerContactID,
                                Mobile: response.Mobile,
                                StatusCode: response.StatusCode
                            }
                            AsyncStorage.setItem('logindetail', JSON.stringify(obj));
                            if (requestArray.length != 0) {
                                requestArray.forEach(x => {
                                    try {
                                        console.log(x, "request Url");
                                        x.headers.Authorization = `Bearer ${response.access_token}`;
                                        x.headers["Content-Type"] = "application/x-www-form-urlencoded";
                                        APIKit.defaults.headers.common["Authorization"] = `Bearer${response.access_token}`;
                                        APIKit(x)
                                    } catch (e) {
                                        console.log(e)
                                    }
                                });
                            }
                            return APIKit(originalRequest);
                        }
                    })
                    .catch(err => {
                        console.log(err);
                    });
            }
        }
        return Promise.reject(error);
    }
);

export default APIKit;

Home.js

gettingToken = async () => {
    let userToken = await AsyncStorage.getItem('userToken');
    userToken = JSON.parse(userToken);
    await setClientToken(userToken);
}
Related