Axios Request and Response Interceptors

Axios interceptors come in handy when we need to track, register, or work with:
1. Requests before leaving 
2. Responses before arriving
3. Both 1 and 2

Thanks to interceptors, we can pass our own handlers/callbacks for the following cases: 
1. Before launching a request
2. Catching an error at HTTP request launch
3. Arrival of a response
4. Catching an error at response arrival

This is an example of how we can use them: 

```
// handlers for the request launch and for catching request launch error
axios.interceptors.request.use(
   (config) => {
      console.log('We are now preparing to launch the request!')
      return config;
   },
   (error) => Promise.reject(error),
);

// handlers for response interceptor and error response interceptor
axios.interceptors.response.use(
     (response) => {
        console.log('We received the response!');
        return response;
     },
     (error) => {
       if (error.response.status === 403) 
         return Promise.reject(error);
     }
},
```

Each Axios Instance can have its custom configuration and request and response interceptors. This can be very useful when the architecture of our application enforces/allows each of our services to use their own Axios Instance. This way, each service can have an Axios Instance with custom configuration and custom request/response interceptors. 

This is how an Axios Instance factory utility function could look like in TypeScript: 

```
export const createAxiosWithInterceptors = (
  requestConfig: AxiosRequestConfig = {},
  requestInterceptorHandlers: Partial<RequestInterceptorHandlers> = DefaultRequestInterceptor,
  responseInterceptorHandlers: Partial<ResponseInterceptorHandlers> = DefaultResponseInterceptor,
) => {
  const axiosInstance = axios.create(requestConfig);

  axiosInstance.interceptors.request.use(
    requestInterceptorHandlers.requestConfigHandler,
    requestInterceptorHandlers.requestErrorHandler,
  );

  axiosInstance.interceptors.response.use(
    responseInterceptorHandlers.responseHandler,
    responseInterceptorHandlers.responseErrorHandler,
  );

  return axiosInstance;
};
```

where we can define our types and default interceptors as follows: 

```
type RequestConfigHandler = (config: AxiosRequestConfig) => AxiosRequestConfig;
type RequestErrorHandler = (error: AxiosError) => Promise<AxiosError>;
type RequestInterceptorHandlers = {
  requestConfigHandler: RequestConfigHandler;
  requestErrorHandler: RequestErrorHandler;
};

type ResponseHandler = (response: AxiosResponse) => AxiosResponse;
type ResponseErrorHandler = (error: AxiosError) => Promise<AxiosError>;

type ResponseInterceptorHandlers = {
  responseHandler: ResponseHandler;
  responseErrorHandler: ResponseErrorHandler;
};

const DefaultResponseInterceptor = {
  responseHandler: (response: AxiosResponse) => response,
  responseErrorHandler: (error: AxiosError) => Promise.reject(error),
};

const DefaultRequestInterceptor = {
  requestConfigHandler: (config: AxiosRequestConfig) => config,
  requestErrorHandler: (error: AxiosError) => Promise.reject(error),
};
```

In each of our services we can then use our factory method as follows: 

```
// axios instance with custom request config received through constructor, custom request interceptor and default response interceptor
protected http: AxiosInstance = createAxiosWithInterceptors(
    this.requestConfig,
    {
      requestConfigHandler: (config) => {
        console.log();
        return config;
      },
    },
);

```

Rares Raulea
05 Jan 2022
« Back to post