Mastering Axios Interceptors

Axios is a powerful HTTP client for JavaScript that allows you to easily interact with APIs. One of its most useful features is interceptors, which let you run custom code before a request is sent or after a response is received. This guide will walk you through everything you need to know about Axios interceptors, including their types and several practical use cases.

Types of Axios Interceptors

Request Interceptors

Request interceptors are executed before a request is sent. They allow you to modify the request configuration or handle errors. The config parameter represents the request configuration object, which includes details like the URL, method, headers, and data.

axios.interceptors.request.use(
  function (config) {
    // Modify the request config here
    return config
  },
  function (error) {
    // Handle the request error here
    return Promise.reject(error)
  },
)

The shape of the config object typically includes:

  • url: The URL of the request.
  • method: The HTTP method (GET, POST, etc.).
  • headers: An object representing the request headers.
  • params: URL parameters.
  • data: The payload for POST/PUT requests.

For example:

{
  "url": "/api/data",
  "method": "get",
  "headers": {
    "Authorization": "Bearer token"
  },
  "params": {
    "id": 1
  },
  "data": {
    "name": "John Doe"
  }
}

Response Interceptors

Response interceptors are executed after a response is received. They allow you to modify the response data or handle errors.

axios.interceptors.response.use(
  function (response) {
    // Modify the response data here
    return response
  },
  function (error) {
    // Handle the response error here
    return Promise.reject(error)
  },
)

Use Cases for Axios Interceptors

1. Adding Authorization Headers

You often need to include an authorization token in your request headers for authenticated API calls. Request interceptors can be used to automatically attach this token.

axios.interceptors.request.use(
  function (config) {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  function (error) {
    return Promise.reject(error)
  },
)

2. Handling Unauthorized Responses

You might want to handle unauthorized responses globally by redirecting the user to a login page or displaying an error message.

axios.interceptors.response.use(
  function (response) {
    return response
  },
  function (error) {
    if (error.response && error.response.status === 401) {
      // Redirect to login page or show an error message
      window.location.href = '/login'
    }
    return Promise.reject(error)
  },
)

3. Logging Requests and Responses

For debugging purposes, it's often useful to log details of requests and responses. You can use interceptors to log this information.

axios.interceptors.request.use(
  function (config) {
    console.log('Request:', config)
    return config
  },
  function (error) {
    console.error('Request error:', error)
    return Promise.reject(error)
  },
)

axios.interceptors.response.use(
  function (response) {
    console.log('Response:', response)
    return response
  },
  function (error) {
    console.error('Response error:', error)
    return Promise.reject(error)
  },
)

4. Retrying Failed Requests

In case of a failed request, you might want to retry it automatically. This can be achieved using response interceptors. In this example, we create a separate axiosInstance to avoid modifying the default Axios instance, providing better isolation and control over the retry logic.

We also set a fixed number of retry attempts to avoid an infinite loop, which is a bad practice.

import axios from 'axios'

const axiosInstance = axios.create() // Create a separate Axios instance

const MAX_RETRY_ATTEMPTS = 3 // Set a fixed number of retry attempts

axiosInstance.interceptors.response.use(
  function (response) {
    return response
  },
  function (error) {
    const config = error.config
    if (!config.__retryCount) {
      config.__retryCount = 0
    }

    if (config.__retryCount < MAX_RETRY_ATTEMPTS) {
      config.__retryCount += 1
      return axiosInstance(config)
    }

    return Promise.reject(error)
  },
)

In this example:

  • We create an axiosInstance to encapsulate the retry logic separately.
  • We define a constant MAX_RETRY_ATTEMPTS to limit the number of retry attempts to 3.
  • We use config.__retryCount to track the number of retry attempts for each request.
  • The interceptor retries the request if the retry count is less than the maximum allowed attempts.

5. Displaying Toast Notifications for Success and Failure

You can show toast notifications to inform users about the success or failure of API calls using interceptors.

import axios from 'axios'
import { toast } from 'react-toastify'

// Set up request interceptor
axios.interceptors.request.use(
  function (config) {
    return config
  },
  function (error) {
    toast.error('Request failed')
    return Promise.reject(error)
  },
)

// Set up response interceptor
axios.interceptors.response.use(
  function (response) {
    toast.success('Request succeeded')
    return response
  },
  function (error) {
    toast.error('Request failed')
    return Promise.reject(error)
  },
)

Conclusion

Axios interceptors provide a powerful way to handle global concerns like adding authorization headers, handling errors, logging, retrying requests, and displaying notifications. By leveraging these interceptors, you can make your code more modular, maintainable, and easier to debug. Experiment with the examples provided and incorporate interceptors into your projects to take full advantage of Axios's capabilities.

Remember, interceptors can be a double-edged sword if not used carefully. Always ensure that the logic within interceptors is performant and error-free to avoid unexpected issues in your applications.