Intercepting http requests in Axios

Kenneth Coffie

October 05, 2021

4 min read

One of the first libraries I reach for when anything sort of web application is axios. Axios is an http client for making requests in the browser and Node.js. One might ask, why not use the fetch api that is already built into all modern browsers? My answer is always interceptors.

Having previously worked with Angular in my past jobs, one of the niceties its' built-in client offers is the ability to capture or intercept outgoing http requests. Since React doesn't care about which libraries we use with our UI, we have to make these decisions on our own.

This means choosing something like which http client we are using is left to us and luckily enough there are plethora of options in the Javascript eco-system. Some of these include got, axios, superagent, needle and the built-in fetch api. Chances are, any of the listed options will work fine for you but sometimes you might need to intercept outgoing requests.

Why intercept?

Sometimes you want to transform an outgoing request or read some data from an http response. To be more specific, one of the most common use cases for transforming requests is to attach a token to subsequent requests after signing in.

The code for this should look like this:

jsx
import axios from 'axios'
function getToken(){
// code to retrieve jwt token from cookies or localstorage
}
axios.interceptors.request.use(req => {
const token = getToken()
req.defaults.common['authorisation'] = `Bearer ${token}`
return request
}, err => Promise.reject(err))

If you're using jwt tokens to authenticate users into your application, you might also be using refresh tokens to persist user session. A refresh token is a token that is sent by your server when the jwt token is expired. Once your server has sent over the token, you might want to accept it and return.

jsx
axios.interceptors.response.use(req => {
const token = getToken()
req.defaults.headers.common["Authorization"] = `Bearer ${token}`
return request
}, err => Promise.reject(err))

Aside from intercepting and reading the refresh token from the response headers, we could also intercept the response error incase there is any incoming error. You could also check for certain http codes in order to perform certain actions in your frontend when that error occurs at the interceptor level rather than call by call basis.

Gotcha

One of the most painful experiences I have ever had whilst working with axios interceptors was handling the error callback.

jsx
axios.interceptors.response.use(req => {
return request
}, err => Promise.reject(err))
jsx
axios.interceptors.response.use(req => {
return request
}, err => err)

Do you notice any difference between the two segments of code? The difference is that we are handling the error in the first segment and not handling it in the second. If you were to write a try/catch block around a normal request that had the second interceptor initiated, you would not see the error occur in the catch block. Instead the error will acts as a normal response in the try block. The internals of why this happens is beyond my purview but debugging it drove me crazy for weeks, dare I say months.

Conclusion

I hope you have learnt how to work with interceptors and the concept behind them. That said, interceptors aren't the other way to configure defaults for outgoing or incoming requests and response in JavaScript. Before you reach for an interceptor, try to see if what you are building can be implemented with an Axios instance or mutating the Axios object via axios.defaults. The syntax for an Axios instance looks like this:

jsx
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});

If you prefer working with the fetch api but still love the concept of interceptors, you can check out fetch-interceptor or if you are up for it you can write your own.

Subscribe to my newsletter

You won't receive any spam! ✌️