DEV Community

Cover image for Seamlessly Handling API 401 Errors in React Native: Automatic Token Refresh with Axios Interceptors
Amit Kumar
Amit Kumar

Posted on

Seamlessly Handling API 401 Errors in React Native: Automatic Token Refresh with Axios Interceptors

In modern mobile applications, API requests are the backbone for fetching data, user interactions, and other crucial functionalities. One common challenge arises when your app faces an expired token, which results in a 401 Unauthorized error.

In this article, we’ll walk you through handling such errors silently using Axios interceptors without disrupting the user experience. Instead of forcing the user to re-login, we can refresh the token and retry the failed requests automatically.

Why Refresh the Token?

Tokens are often used to authenticate users, and these tokens can expire over time, leading to a 401 Unauthorized response from your API. When this happens, we can refresh the token in the background and retry the failed requests. This method avoids disrupting the user with unnecessary logins or errors.

Setting Up Axios for API Requests

To start, we will define four basic API request functions: getRequest, postRequest, updateRequest, and deleteRequest. Each function handles its respective HTTP method.

export const getRequest = async (path, params) => {};
export const postRequest = async (path, params) => {};
export const updateRequest = async (path, params) => {};
export const deleteRequest = async (path, params) => {};
Enter fullscreen mode Exit fullscreen mode

These functions will use Axios to send the requests, but we need to manage what happens when a 401 error occurs.

Handling Token Refresh

To handle token refresh and avoid multiple refresh requests at the same time, we’ll create a flag to manage this process and a queue to store requests that fail due to a 401 Unauthorized error. The queue allows us to retry them once the token has been refreshed.

Step 1: Define the Flag and Queue
First, we’ll define a flag to prevent multiple token refresh requests from happening simultaneously, as well as a queue to hold failed requests.

// Flag to prevent multiple token refresh requests
let isRefreshing = false;

// Create a list to hold the request queue
const refreshAndRetryQueue = [];

Enter fullscreen mode Exit fullscreen mode

Step 2: Set Up the Axios Interceptor
Next, we add an Axios interceptor to catch any 401 Unauthorized errors. If the request fails due to a 401, we attempt to refresh the token, retry the original request, and resolve any queued requests with the new token.

// Axios interceptor
axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    if (error.response && error.response.status === 401) {
      if (!isRefreshing && !originalRequest._retry) {
        isRefreshing = true;

        try {
          // Refresh the access token
          const newAccessToken = await refreshToken();

          if (newAccessToken) {
            originalRequest._retry = true;

            // Update the request headers with the new access token
            originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;

            // Retry all requests in the queue with the new token
            refreshAndRetryQueue.forEach(({ config, resolve, reject }) => {
              axios
                .request(config)
                .then((response) => resolve(response))
                .catch((err) => reject(err));
            });

            // Clear the queue
            refreshAndRetryQueue.length = 0;

            // Retry the original request
            return axios(originalRequest);
          }
        } catch (err) {
          // Handle token refresh error: clear storage or redirect to login
        } finally {
          isRefreshing = false;
        }
      }

      // Add the original request to the queue
      return new Promise((resolve, reject) => {
        refreshAndRetryQueue.push({ config: originalRequest, resolve, reject });
      });
    }

    return Promise.reject(error);
  }
);

Enter fullscreen mode Exit fullscreen mode

Step 3: Refresh Token Function
You’ll need to implement the refreshToken function, which sends a request to your server to get a new access token. This function will handle the actual token refresh process.

export const refreshToken = async () => {
  // Make API call to refresh the token
  try {
    const response = await axios.post('/auth/refresh-token', {
      // Provide necessary refresh token details
    });
    const { accessToken } = response.data;

    // Save the new token for future requests
    // e.g., localStorage.setItem('accessToken', accessToken);

    return accessToken;
  } catch (error) {
    console.error("Failed to refresh token", error);
    return null;
  }
};

Enter fullscreen mode Exit fullscreen mode

Step 4: Integrating Token Refresh into API Requests
Now that we have our interceptor set up, we can modify our getRequest, postRequest, updateRequest, and deleteRequest functions to handle the token refresh logic seamlessly. This is how you’d handle a 401 Unauthorized error in the getRequest function:

export const getRequest = async (path, params) => {
  try {
    const response = await axios.get(path, {
      params,
      headers: {
        'Access-Control-Allow-Origin': '*',
        Authorization: `Bearer ${localStorage.getItem('accessToken')}`, // Get token from storage
      },
    });

    if (response.status >= 200 && response.status < 300) {
      return await response.data;
    } else {
      throw new Error(`${response.status}`);
    }
  } catch (error) {
    // Log error and handle 401 scenarios
    callAnalyticsAndToastMsg('GET', path, error);

    if (error.response.status === 401) {
      callAnalyticsAndToastMsg('GET', path, error);
    }

    throw error;
  }
};

Enter fullscreen mode Exit fullscreen mode

Similarly, this pattern can be applied to postRequest, updateRequest, and deleteRequest.

Step 5: Handling Multiple Failed Requests

If multiple requests fail due to token expiration, they will be queued and retried once the token is successfully refreshed. The Axios interceptor ensures that no user action is required to refresh the token or retry the requests. This happens automatically in the background, keeping the user experience smooth and seamless.

Conclusion

By leveraging Axios interceptors and refreshing tokens automatically, you can handle 401 Unauthorized errors without disrupting the user experience. This approach ensures that expired tokens are refreshed in the background and all failed requests are retried automatically. Implementing this strategy keeps your app secure while providing a frictionless user experience.

Try integrating this method into your React Native API handling code and enjoy a smoother flow for your users!

Top comments (0)