Handling Bearer Tokens in Axios for React Projects
When building a React application, Axios is a popular choice for managing HTTP requests. It works seamlessly with mock APIs during development, but challenges often arise when connecting to a real backend server. One common issue is receiving 403 Forbidden errors because the server requires a bearer token in the request header.
The Problem
During development with mock responses, GET, POST, PUT, and DELETE calls may run smoothly. However, once connected to a live backend, requests can fail if they lack proper authentication headers. In many cases, the backend expects a bearer token to validate the client.
The Solution: A Common Axios Service
To address this, you can create a common Axios service that defines shared configurations such as timeout and content type.
const axiosInstance = axios.create({
baseURL: apiUrl,
timeout: 5000,
headers: {
'Content-Type': 'application/json',
},
});
More importantly, this service can automatically attach the bearer token to every outgoing request.
axiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
This setup ensures the token is added to every request, regardless of where it is stored (cookie, localStorage, etc.).
Handling Responses
For responses, you can simply pass through what the server returns and the caller will handle the response whether it’s successful or not.
axiosInstance.interceptors.response.use(
response => response,
error => {
return Promise.resolve(error.response);
}
);
However, you can also add custom logic. For example, you might want to clear login details if a 403
axiosInstance.interceptors.response.use(
response => response,
async (error) => {
if (error.response && error.response.status === 403) {
localStorage.clear();
window.location.href = "/login";
}
return Promise.resolve(error.response);
}
);
This approach not only handles authentication but also ensures that invalid sessions are cleaned up automatically.
Complete Service for reference
import axios from 'axios';
const apiUrl = import.meta.env.VITE_API_URL;
const axiosInstance = axios.create({
baseURL: apiUrl,
timeout: 5000,
headers: {
'Content-Type': 'application/json',
},
});
axiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
axiosInstance.interceptors.response.use(
response => response,
async (error) => {
if (error.response && error.response.status === 403) {
localStorage.clear();
window.location.href = "/login";
}
return Promise.resolve(error.response);
}
);
export default axiosInstance;
Using the Service in Components
With the service in place, making API calls becomes straightforward:
import axiosService from './axiosService';
axiosService.get('/users')
.then(res => console.log(res.data))
.catch(err => console.error(err));
By creating a reusable Axios service, you can:
- Standardize configuration across your project.
- Automatically attach bearer tokens to requests.
- Handle authentication errors gracefully.
- This pattern simplifies API integration in React projects and makes your application more secure and maintainable.



Post Comment