Working with Axios and interceptors. Setting up refresh token with interceptors.

Pre-requisites

  • Understanding of basic javascript
  • Understanding of setting up react project

What you will learn ?

  • structured setup axios
  • setting up interceptor
  • using interceptor for request token

For all of you who are new to axios, Axios is promise based Http Client for node js and the browser. On the server-side it uses the native node.js http module, while on the client (browser) it uses XMLHttpRequests. One more thing about axios I like is when you recieve a data through an api call, you don't need to parse it unlike fetch.

Here I am showing you setting up axios on the react project. in order to do so lets open the terminal and type

```npx create-react-app my-app


After react project finishes installing dependencies. Type 

```npm i axios jwt-decode dayjs

This will install the axios and other packages about which we will talk about a bit later. Now in the src folder we will create a utils folder. Inside the utils folder we keep all the important logics which are needed through out the project. Now, inside the utility folder add a file with the name axiosInstance.js.

Inside this file we will add the access token and refresh token logic through interceptors. Interceptors are helpers which are used to intercept the requests or responses before they are handled by then and catch block.

In our scenario we will use interceptors to attach access token in the headers before every api call and incase the access token expires we will use refresh token to fetch new access token and attach the same to the api calls we make.

Whenever we have login functionality we have the option to store access token in various places like cookies, session storage or local storage. In this case lets assume that the token has been saved in the local storage.

Now we will write the code step by step..

import Axios from "axios";
import dayjs from "dayjs";
import jwt_decode from "jwt-decode";

const v1Url = baseUrl;
let authToken = localStorage.getItem("access_token") ? localStorage.getItem("access_token") : null

const $axiosProtected = Axios.create({
  headers: {  "Authorization": `Bearer ${authToken}` },
  timeout: 10000,
  baseURL: v1Url,
});

export default $axiosProtected;

Now, whenever we want to call the api with base url we can import $axiosProtected from axios instance module and use it as base url. We don't need to add token on every call as we have added it in the $axiosProtected.

But now we need refresh token logic so that whenever the access token expires we will use the refresh token to make a request for new access token add and add it in the the api request which we make.

import Axios from "axios";
import dayjs from "dayjs";
import jwt_decode from "jwt-decode";

const v1Url = baseUrl;
let authToken = localStorage.getItem("access_token") ? localStorage.getItem("access_token") : null

const $axiosProtected = Axios.create({
  headers: {  "Authorization": `Bearer ${authToken}` },
  timeout: 10000,
  baseURL: v1Url,
});

$axiosProtected.interceptors.request.use(async (req: any) => {

  if(!authToken){
      req.headers.Authorization = `Bearer ${authToken}`
  }
  // @ts-ignore
  const user:any = jwt_decode(authToken)
const isExpired = dayjs.unix(user.exp).diff(dayjs()) < 1
if(!isExpired) {
  return req
} else {
  const refreshToken = localStorage.getItem("refresh_token");
  const response: any = await Axios.post(REFRESH_TOKEN_URL, {
    refreshToken
  })

  localStorage.setItem("refresh_token", response?.data?.data.refreshToken);
  localStorage.setItem("access_token", response?.data?.data.token);
  req.headers.Authorization = `Bearer ${token}`
}
  return req;
});

export default $axiosProtected;

On the above code we will check whether the token has expired or not. If the token expired then we will call the the refresh token and store the new access token on the local storage and also add the new access token on the api header before making the request.

Now we also need to handle the situation in which the refresh token also expires following is the code to handle that.

$axiosProtected.interceptors.response.use((response) => {
  return response
}, async function (error) {
  const originalRequest = error.config;
if (error.response.status === 401 && originalRequest.url.includes("/refresh-token")) {
      localStorage.removeItem("access_token");
      localStorage.removeItem("refresh_token");
         window.location.href = "/login";
    }
   if(error.response.status===401 && !originalRequest._retry){
      originalRequest._retry = true;
      const refresh_Token = localStorage.getItem("refresh_token");
      return Axios.post("http://localhost:5000/v1/user/refresh-token", {
        refresh_Token 
      }).then((resp: any) => {
        if(resp.status===200){
          const {token, refreshToken} = resp?.data?.data;
          localStorage.setItem("refresh_token", refreshToken);
          localStorage.setItem("access_token", token);
          $axiosProtected.defaults.headers.common['Authorization'] = 'Bearer ' + 
           localStorage.getItem('access_token');
          return Axios(originalRequest);
        }
      })
    }
  return Promise.reject(error);
});

In the above code, if the refresh token fails we just empty the local storage. Otherwise we call the refresh token api again for new access token.

Now to call the api we can use

const result = await $axiosProtected.post(url, data)

Note: This is one way of handling the refresh token logic but there are many other ways to handle the refresh token.