168

I have seen axios documentation, but all it says is

// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
  // Do something with response data
  return response;
}, function (error) {
  // Do something with response error
  return Promise.reject(error);
});

Also many tutorials only show this code but I am confused what it is used for, can someone please give me simple example to follow.

yachaka
  • 5,429
  • 1
  • 23
  • 35
Akash Salunkhe
  • 2,698
  • 2
  • 16
  • 31

7 Answers7

240

To talk in simple terms, it is more of a checkpoint for every HTTP action. Every API call that has been made, is passed through this interceptor.

So, why two interceptors?

An API call is made up of two halves, a request, and a response. Since it behaves like a checkpoint, the request and the response have separate interceptors.

Some request interceptor use cases -

Assume you want to check before making a request if your credentials are valid. So, instead of actually making an API call, you can check at the interceptor level that your credentials are valid.

Assume you need to attach a token to every request made, instead of duplicating the token addition logic at every Axios call, you can make an interceptor that attaches a token on every request that is made.

Some response interceptor use cases -

Assume you got a response, and judging by the API responses you want to deduce that the user is logged in. So, in the response interceptor, you can initialize a class that handles the user logged in state and update it accordingly on the response object you received.

Assume you have requested some API with valid API credentials, but you do not have the valid role to access the data. So, you can trigger an alert from the response interceptor saying that the user is not allowed. This way you'll be saved from the unauthorized API error handling that you would have to perform on every Axios request that you made.

Here are some code examples

The request interceptor

  • One can print the configuration object of axios (if need be) by doing (in this case, by checking the environment variable):

    const DEBUG = process.env.NODE_ENV === "development";
    
    axios.interceptors.request.use((config) => {
        /** In dev, intercepts request and logs it into console for dev */
        if (DEBUG) { console.info("✉️ ", config); }
        return config;
    }, (error) => {
        if (DEBUG) { console.error("✉️ ", error); }
        return Promise.reject(error);
    });
    
  • If one wants to check what headers are being passed/add any more generic headers, it is available in the config.headers object. For example:

    axios.interceptors.request.use((config) => {
        config.headers.genericKey = "someGenericValue";
        return config;
    }, (error) => {
        return Promise.reject(error);
    });
    
  • In case it's a GET request, the query parameters being sent can be found in config.params object.

The response interceptor

  • You can even optionally parse the API response at the interceptor level and pass the parsed response down instead of the original response. It might save you the time of writing the parsing logic again and again in case the API is used in the same way in multiple places. One way to do that is by passing an extra parameter in the api-request and use the same parameter in the response interceptor to perform your action. For example:

    //Assume we pass an extra parameter "parse: true" 
    axios.get("/city-list", { parse: true });
    

    Once, in the response interceptor, we can use it like:

    axios.interceptors.response.use((response) => {
        if (response.config.parse) {
            //perform the manipulation here and change the response object
        }
        return response;
    }, (error) => {
        return Promise.reject(error.message);
    });
    

    So, in this case, whenever there is a parse object in response.config, the manipulation is done, for the rest of the cases, it'll work as-is.

  • You can even view the arriving HTTP codes and then make the decision. For example:

    axios.interceptors.response.use((response) => {
        if(response.status === 401) {
             alert("You are not authorized");
        }
        return response;
    }, (error) => {
        if (error.response && error.response.data) {
            return Promise.reject(error.response.data);
        }
        return Promise.reject(error.message);
    });
    
Aseem Upadhyay
  • 4,279
  • 3
  • 16
  • 36
  • 2
    Where do you write these interceptors - on each component or on a global/central location for the whole app? – James Poulose Apr 03 '20 at 21:16
  • @JamesPoulose it should be in a central location since its behavior is pretty straightforward. it can be written at a component level as well, but that generally leads to code duplication. which wouldn't be wise – Aseem Upadhyay Apr 10 '20 at 15:35
  • I ended up putting it in `index.js`, but don't think that's the right place - thoughts? – James Poulose Apr 10 '20 at 17:52
  • 1
    @JamesPoulose you can separate out your service layer, and have the interceptor at the base of that service layer, something like `services/index.js`. so the frontend app stays at one place, and the services stay separated and platform-agnostic :D – Aseem Upadhyay Apr 11 '20 at 08:15
  • 2
    @AseemUpadhyay I placed this code in `services/index.js` and `export default axios` and then `import axios from "services/index.js"` wherever I use axios. But I see others on the internet create a wrapper for axios instead… which approach do you personally use? Thanks – Raj May 08 '20 at 08:29
  • @Raj there's no harm in that. but creating a wrapper or using an instance of axios helps in having a single implementation with multiple apis and allows extension for whichever case is valid. say, if you want to create two instances of axios with different headers, you can do that with minimal changes. – Aseem Upadhyay May 11 '20 at 07:46
  • Isn't what I described having done means I created an instance of axios? Or are you referring to something else? – Raj May 11 '20 at 17:34
  • 1
    @JamesPoulose I solved this issue by creating a "services" folder under "src" folder. In this directory I'll create a "request.js" or any custom name for writing interceptor. – nima Sep 13 '20 at 13:24
  • @AseemUpadhyay: is it possibile to define two request interceptors and concatenate them? For instance ```axios.interceptors.request.use(validateToken); axios.interceptors.request.use(setToken);```? Or does axios support only one request interceptor? – Matteo Piazza Jun 29 '21 at 15:41
  • @MatteoPiazza afaik you can't create two diff interceptors. But from what i see, it's basically some additional checks you're putting in place. We could call those functions in the intercetpor itself :D – Aseem Upadhyay Jun 30 '21 at 05:12
  • @AseemUpadhyay If I changed baseURL using interceptor then how can I write Jest unit test to validate with what url request is called ? I have mocked axios and in code passed additional url part and interceptor changes baseUrl as per need – sagar Oct 08 '21 at 13:33
  • Hi @AseemUpadhyay, I want to refresh access token if it is expired. Should I check Id token and refresh it before sending request with interceptors.request or just check the response and then refresh. Which one is better way?Thank you:) – aylin Aug 11 '22 at 07:07
  • Can an interceptor on request return directly without passing the request onto the actual handler even when there is no error? I want to deliberately response a request directly within an interceptor when some condition is met and stop passing the request to the actual handler. Is this possible? – dragonfly02 Aug 16 '22 at 00:46
43

You can use this code for example, if you want to catch the time that takes from the moment that the request was sent until the moment you received the response:

const axios = require("axios");

(async () => {
  axios.interceptors.request.use(
    function (req) {
      req.time = { startTime: new Date() };
      return req;
    },
    (err) => {
      return Promise.reject(err);
    }
  );

  axios.interceptors.response.use(
    function (res) {
      res.config.time.endTime = new Date();
      res.duration =
        res.config.time.endTime - res.config.time.startTime;
      return res;
    },
    (err) => {
      return Promise.reject(err);
    }
  );

  axios
    .get("http://localhost:3000")
    .then((res) => {
      console.log(res.duration)
    })
    .catch((err) => {
      console.log(err);
    });
})();
Steve.g
  • 448
  • 4
  • 5
  • 7
    I upvoted your response because you showed both the interceptors and the actual call in one place. I was having a hard time putting the two together. – Woodsman Dec 30 '20 at 06:33
  • I was also looking for an example that puts the two together, and it clearly shows that if you're just wanting to add interceptors, your original call doesn't have to change. – Qiniso Oct 29 '22 at 08:04
23

It is like a middle-ware, basically it is added on any request (be it GET, POST, PUT, DELETE) or on any response (the response you get from the server). It is often used for cases where authorisation is involved.

Have a look at this: Axios interceptors and asynchronous login

Here is another article about this, with a different example: https://medium.com/@danielalvidrez/handling-error-responses-with-grace-b6fd3c5886f0

So the gist of one of the examples is that you could use interceptor to detect if your authorisation token is expired ( if you get 403 for example ) and to redirect the page.

Constantin Chirila
  • 1,979
  • 2
  • 19
  • 33
9

I will give you more practical use-case which I used in my real world projects. I usually use, request interceptor for token related staff (accessToken, refreshToken), e.g., whether token is not expired, if so, then update it with refreshToken and hold all other calls until it resolves. But what I like most is axios response interceptors where you can put your apps global error handling logic like below:

httpClient.interceptors.response.use(
  (response: AxiosResponse) => {
    // Any status code that lie within the range of 2xx cause this function to trigger
    return response.data;
  },
  (err: AxiosError) => {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    const status = err.response?.status || 500;
    // we can handle global errors here
    switch (status) {
      // authentication (token related issues)
      case 401: {
        return Promise.reject(new APIError(err.message, 409));
      }

      // forbidden (permission related issues)
      case 403: {
        return Promise.reject(new APIError(err.message, 409));
      }

      // bad request
      case 400: {
        return Promise.reject(new APIError(err.message, 400));
      }

      // not found
      case 404: {
        return Promise.reject(new APIError(err.message, 404));
      }

      // conflict
      case 409: {
        return Promise.reject(new APIError(err.message, 409));
      }

      // unprocessable
      case 422: {
        return Promise.reject(new APIError(err.message, 422));
      }

      // generic api error (server related) unexpected
      default: {
        return Promise.reject(new APIError(err.message, 500));
      }
    }
  }
);
Humoyun Ahmad
  • 2,875
  • 4
  • 28
  • 46
6

How about this. You create a new Axios instance and attach an interceptor to it. Then you can use that interceptor anywhere in your app

export const axiosAuth = axios.create()

//we intercept every requests 
axiosAuth.interceptors.request.use(async function(config){
    //anything you want to attach to the requests such as token 
    return config;
}, error => {
    return Promise.reject(error)
})


//we intercept every response
axiosAuth.interceptors.response.use(async function(config){
    
    return config;
}, error => {
//check for authentication or anything like that
    return Promise.reject(error)
})

Then you use axiosAuth the same way you use axios

Udendu Abasili
  • 1,163
  • 2
  • 13
  • 29
5

This is the way I used to do in my project. The code snippet refers how to use access and refresh token in the axios interceptors and will help to implements refresh token functionalities.

const API_URL =
    process.env.NODE_ENV === 'development'
        ? 'http://localhost:8080/admin/api'
        : '/admin-app/admin/api';

const Service = axios.create({
    baseURL: API_URL,
    headers: {
        Accept: 'application/json',
    },
});

Service.interceptors.request.use(
    config => {
        const accessToken = localStorage.getItem('accessToken');
        if (accessToken) {
            config.headers.common = { Authorization: `Bearer ${accessToken}` };
        }
        return config;
    },
    error => {
        Promise.reject(error.response || error.message);
    }
);

Service.interceptors.response.use(
    response => {
        return response;
    },
    error => {
        let originalRequest = error.config;
        let refreshToken = localStorage.getItem('refreshToken');
        const username = EmailDecoder(); // decode email from jwt token subject
        if (
            refreshToken &&
            error.response.status === 403 &&
            !originalRequest._retry &&
            username
        ) {
            originalRequest._retry = true;
            return axios
                .post(`${API_URL}/authentication/refresh`, {
                    refreshToken: refreshToken,
                    username,
                })
                .then(res => {
                    if (res.status === 200) {
                        localStorage.setItem(
                            'accessToken',
                            res.data.accessToken
                        );
                        localStorage.setItem(
                            'refreshToken',
                            res.data.refreshToken
                        );

                        originalRequest.headers[
                            'Authorization'
                        ] = `Bearer ${res.data.accessToken}`;

                        return axios(originalRequest);
                    }
                })
                .catch(() => {
                    localStorage.clear();
                    location.reload();
                });
        }
        return Promise.reject(error.response || error.message);
    }
);

export default Service;
3

I have implemented in the following way

httpConfig.js

import axios from 'axios'
import { baseURL } from '../utils/config'
import { SetupInterceptors } from './SetupInterceptors'


const http = axios.create({
    baseURL: baseURL
})

SetupInterceptors(http)

export default http

SetupInterceptors.js

import { baseURL } from '../utils/config'


export const SetupInterceptors = http => {

    http.interceptors.request.use(
        config => {
            config.headers['token'] = `${localStorage.getItem('token')}`
            config.headers['content-type'] = 'application/json'
            return config
        },
        error => {
            return Promise.reject(error)
        }
    )

    http.interceptors.response.use(function(response) {
        return response
    }, function (error) {
        const status = error?.response?.status || 0
        const resBaseURL = error?.response?.config?.baseURL
        if (resBaseURL === baseURL && status === 401) {
            if (localStorage.getItem('token')) {
                localStorage.clear()
                window.location.assign('/')
                return Promise.reject(error)
            } else {
                return Promise.reject(error)
            }
        }
        return Promise.reject(error)
    })
}

export default SetupInterceptors

Reference : link

KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133