While the native fetch API handles basic requests adequately, mature applications demand a much more robust HTTP client. Axios provides a superior API with request and response interceptor capabilities that act as middleware for every network call that leaves your browser. A request interceptor allows you to inspect, modify, or entirely cancel a request before it reaches the server, which is the perfect hook to inject your JWT token into the Authorization header automatically. Meanwhile, a response interceptor lets you globally handle errors like expired tokens, network failures, or server maintenance without cluttering your UI components with repetitive try/catch blocks. By centralizing this logic, Axios becomes the nervous system of your application, ensuring every interaction with the backend is smooth and secure.
Installing Axios
npm install axios
Creating an Axios Instance
Instead of writing the full http://localhost:5000/api URL in every component, we create a pre-configured instance. This instance holds the base URL and default headers, ensuring consistency across your entire codebase.
// utils/axiosInstance.js
import axios from "axios";
const axiosInstance = axios.create({
baseURL: "http://localhost:5000/api",
timeout: 10000, // 10 seconds
headers: {
"Content-Type": "application/json",
},
});
export default axiosInstance;
Request Interceptor --- Automatically Attaching the Token
This interceptor runs right before any request is sent. It pulls the JWT from localStorage and attaches it to the Authorization header. This means your components don't need to remember to pass the token—Axios handles it invisibly for every secured endpoint.
// utils/axiosInstance.js (continued)
axiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem("accessToken");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
Response Interceptor --- Global Error Handling & Token Expiry
This interceptor catches the response before it reaches your component's .catch() block. If the server returns a 401 Unauthorized status (meaning the token is invalid or expired), we can clear the user session and redirect to the login page in a single centralized location. This saves you from writing if (error.status === 401) in every single API call you make.
jsx
// utils/axiosInstance.js (continued)
axiosInstance.interceptors.response.use(
(response) => response, // Just pass successful responses through
async (error) => {
const originalRequest = error.config;
// Check if error is 401 and we haven't retried yet
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// Optional: Refresh token logic could go here
// For now, we just log out the user
localStorage.removeItem("accessToken");
localStorage.removeItem("user");
// Redirect to login page (React Router navigation will be triggered via event)
window.location.href = "/login";
return Promise.reject(error);
}
return Promise.reject(error);
}
);