How to use axios professionally in the projects

1. Install axios package.

npm install axios

2. Create a folder with name “services” in the project directory. Usually frontend projects have src directory so create “services” folder inside src folder.

3. In the services folder create a file with name “http-client.js” and create axios instance and export that instance.

import axios from "axios";

export const api = axios.create({
    baseURL: 'https://dummyjson.com/', // Replace this url with base url of the api endpoints
});

4. Add interceptor to axios instance if you want to edit the request (e.g. sending authorization header) before it sent to the server or handling api errors from one place.

import axios from "axios";

export const api = axios.create({
  baseURL: "https://dummyjson.com/", // Replace this with base url of the apis endpoints
});

// Add a request interceptor
api.interceptors.request.use(
  (config) => {
    // Do something before request is sent
    // Example: Add  authorization header to config object
    return config;
  },
  (error) => {
    // Do something with request error
    return Promise.reject(error);
  }
);

// Add a response interceptor
api.interceptors.response.use(
  (response) => {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
  },
  (error) => {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    // Example:
    // 1. Showing error message in toast from one place
    // 2. Ask for new access token using refress token
    return Promise.reject(error);
  }
);

Interceptor Example 1: Adding authorization token

import axios from "axios";

export const api = axios.create({
  baseURL: "https://dummyjson.com/", // Replace this with base url of the apis endpoints
});

// Add a request interceptor
api.interceptors.request.use(
  (config) => {
   // Add authorization header
   const token = localStorage.getItem('accessToken');
   if (token) {
     config.headers['Authorization'] = `Bearer ${token}`;
   }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

Interceptor Example 2: Getting new access token using refresh token if session expired

import axios from "axios";

export const api = axios.create({
  baseURL: "https://dummyjson.com/", // Replace this with base url of the apis endpoints
});

// Function to refresh the access token
const refreshAccessToken = async () => {
  try {
    // Make a request to your server to refresh the token
    const response = await axios.post("https://dummyjson.com/refresh", {
      refreshToken: localStorage.getItem("refreshToken"),
    });

    // Update the access token in your local storage
    const newAccessToken = response.data.accessToken;
    localStorage.setItem("accessToken", newAccessToken);

    return newAccessToken;
  } catch (error) {
    // Handle token refresh failure
    console.error("Error refreshing access token:", error);
    throw error;
  }
};

// Add a request interceptor
api.interceptors.request.use(
  (config) => {
    // Add the access token to the request headers
    const token = localStorage.getItem("accessToken");
    if (token) {
      config.headers["Authorization"] = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// Add a response interceptor
api.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    // Check if the error is due to an expired access token
    if (error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      try {
        // Refresh the access token and retry the original request
        const newAccessToken = await refreshAccessToken();
        originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`;

        return axios(originalRequest);
      } catch (refreshError) {
        // If token refresh fails, redirect to the login page or handle it as needed
        console.error(
          "Token refresh failed. Redirecting to login page:",
          refreshError
        );
        // Redirect to login or handle appropriately
      }
    }

    return Promise.reject(error);
  }
);

5. Create a separate files in services folder for specific service, lets assume you want to keep authentication related API calls in user.js file and products related API calls in products.js file.

6. Import the axios instance that we have exported in http-client.js file in the services file product.js and user.js.

A) Code of user.js file

import { api } from "./http-client";

export const login = (data) => api.post('/auth/login',data);

export const getUser = () => api.get('/auth/me');

export const getAllUsers = () =>  api.get('/users');

B) Code of product.js file

import { api } from "./http-client";

export const getAllProducts = () => api.get('/products');

export const getSingleProduct = (productId) =>  api.get(`products/${productId}`);

7. Create another file with name index.js in the services folder.

8. Export all functions that we have created in product.js and user.js file in index.js file so that we can access all these API functions from one place.

// Code in index.js file
export * from "./user.js"
export * from "./product.js"

9. Finally, its time to use our services, please check below code to know how to use these services in the project.

We just have to import functions that we need and make the api call, see the below code.

// Import login functions from index.js file that we have created in the services folder.

import {login} from "./services/index.js" // or just user "./services/", it would also work because of index.js

// Lets say you want to call this function on submit button after form validation
const handlleLogin = () => {
  const formData = {
  email: "your email",
  password: "your password"
  }
  login(formData).then((response)=>{
  // use the response in the code
  })
}

10. In the above code I have not used catch function or try and catch because I am assuming we are handling error from central place and that is axios interceptor we have created earlier. Its totally up to you if you need more control on server side error then you can use try and catch or catch function.

11. If you remember I did named export (API) in http-client.js file. It is because if you have API endpoints which have different base urls then you can create a another axios instance and use that instance in your service file. See the below code.

// Code inside http-client.js
import axios from "axios";

export const api = axios.create({
  baseURL: "https://dummyjson.com/", // Replace this with base url of the apis endpoints
});

export const apiNew = axios.create({
  baseURL: "https://jsonplaceholder.typicode.com/", // Replace this with base url of the apis endpoints
});