DEV Community

Cover image for How to Optimize API Requests with Refresh Tokens and Axios Interceptors in Next.js
Emdadul Islam
Emdadul Islam

Posted on • Edited on

How to Optimize API Requests with Refresh Tokens and Axios Interceptors in Next.js

We all used JWT at least once in our lives. In the minimum scenario we use JWT token for API requests to get any data. This kind of token is know as access token(AT) and it can store some property. But the most important one is expire time. So, we have to store that AT in the browser so that it can be send again when we make a request to the API. The work can be done from here but the problem arise if the token is valid for a long period of time. For this issue developer started to use refresh token(RT).

  1. Access Token: An access token is short lived token that is send as a bearer token inside the authorization header. The expire time is in between 1 minute to 15 minutes maximum.

  2. Refresh Token: When an access token is expired, we make a call to a specific route for new access token. This route take refresh token as authorization header and do some serious validation to return an access token for API calls within the website and a new refresh token to store for the user in the browser to get the next new access token from the '/refresh' route.

For the front end call we usually use Axios or fetch API for calling the external API for data. For this guideline I am gonna use Axios and NextJS as a reference.

On the base line, Axios is used to make HTTP request to an API and get HTTP response in return. Interceptors are like middleman for this request and response. Whenever you gonna make a request its gonna go through the interceptor first. In that interceptor you can change the header object as you like. In our case AT is need to be add in the authorization header like below.

{
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NGYwNzYzOGFlNjg4MDY4YzY1ODBkNzMiLCJwaG9uZSI6IjAxNSIsInJvbGUiOiJTVVBFUl9BRE1JTiIsImlhdCI6MTcxMDUwMzQ5NSwiZXhwIjoxNzExMTA4Mjk1fQ.STh0ZBiQVV
}
Enter fullscreen mode Exit fullscreen mode

For our convenience, we make a fetcher function. that gonna get the access token from the local storage or cookie and add that token into the authorization header. That gonna look like something like this.

code preview of the fetcher function
Here the checkAuth() function get the header as option. In my application it was a hook that gonna utilize the interceptor mechanism of Axios.

Axios Interceptor

Axios interceptor is like a middleware that gonna intercept the request and response of a http request and modify the header or data as per requirement.

So we made a interceptor that gonna get access token and evaluate if it is expired or not. If it is expired then it gonna call the "/refresh-token" route to get the new valid access token along with refresh token.

code preview of the axios interceptor

Here, I used next auth for my project. NextAuth provides a hook called useSession(). This hook returns the data we stored in the session and a authentication state as status. In the useEffect() hook we called the request interceptor first. First argument of this function is callback function and the second argument takes the error handler. In brief we checked the AT validity then took a decision. if we need to fetch the refresh token we call the useRefreshToken() hook to get the new refresh token and set the info in the session storage using useSession() hook.

But the problem arise when a page has 5 or 10 API call and all of them call the fetcher function thus the useAxiosAuth() hook. So all of them got the expired access token and call the /refresh-token route to get the refresh token.

To resolve this problem we introduce the promise based request solver.

code preview of the queue of the pending requests
addRequestToQueue() will take all the request with invalid access token and place them in the refreshQueue[]. After that we will fetch the new access token from the '/refresh-token' route. Once we got all the AT. then we will resolve all the pending request that is waiting for fetching in the refreshQueue[].


Impact Before and After Implementing the Solution:

🚨 Before Optimization:

  • 5 API calls trigger:
    • 5 requests to the /refresh-token route.
    • 5 invalid API calls to the final API using expired access tokens.

Total server requests: 25 requests for every 5 API calls made.

If you have 1,000 users, that means 25,000 unnecessary requests to the backend server! πŸ˜΅β€πŸ’« 😡

βœ… After Optimization:

  • 5 API calls now result in:
    • 1 request to the /refresh-token route to obtain a new access token.
    • 5 valid API calls to the final API with the new, valid access token.

Total server requests: 6 requests for every 5 API calls made.

For 1,000 users, this means only 6,000 requests, drastically reducing server load.πŸŽ‡ 🎊


Key Benefit:

By implementing this optimization, there’s a 76% reduction in backend API calls! This leads to:

  • Reduced server load and faster response times.
  • Improved user experience with minimal delay.
  • A more scalable solution for high-traffic applications.

This is my first writing in my entire career, I apologize if anything is wrong. Suggest me the point of improvements, that I will truly appreciate.

Thank you.

Top comments (0)