Axios Interceptors allow us to run code automatically before a request is sent or after a response is received. They are very useful in React Native applications for adding JWT tokens, handling common errors, and creating a reusable API system.
An interceptor is a function that Axios executes automatically during the request or response process.
axios.interceptors.request.use(config => {
return config;
});
This allows us to modify or inspect requests before they are sent.
Interceptors are useful when the same logic is required for many API calls.
Axios mainly provides two interceptor types:
axios.interceptors.request.use(...);
axios.interceptors.response.use(...);
A request interceptor runs before Axios sends the request to the server.
axios.interceptors.request.use(config => {
console.log("Request is being sent");
return config;
});
The config object contains information about the request.
Axios request configuration contains information such as the URL, HTTP method, headers, and request data.
{
url: "/students",
method: "get",
headers: {}
}
We can modify this configuration inside a request interceptor.
A request interceptor can add common headers automatically.
axios.interceptors.request.use(config => {
config.headers.Accept = "application/json";
return config;
});
This avoids repeating the same header in every API call.
Protected APIs commonly require a JWT token in the
Authorization header.
config.headers.Authorization =
`Bearer ${token}`;
An interceptor can add this header automatically to protected requests.
axios.interceptors.request.use(config => {
const token = "YOUR_JWT_TOKEN";
if (token) {
config.headers.Authorization =
`Bearer ${token}`;
}
return config;
});
In a real application, the token should be obtained from the application's token storage instead of hard-coding it.
It is better to create a reusable Axios instance for a React Native application.
import axios from "axios";
const api = axios.create({
baseURL: "https://example.com/api"
});
export default api;
This instance can be used throughout the application.
Interceptors can be attached to the custom Axios instance.
const api = axios.create({
baseURL: "https://example.com/api"
});
api.interceptors.request.use(config => {
return config;
});
A response interceptor runs after Axios receives a response.
api.interceptors.response.use(response => {
console.log(response.data);
return response;
});
It can inspect or transform the response before it reaches the calling code.
A response interceptor can also handle errors.
api.interceptors.response.use(
response => response,
error => {
console.log(error);
return Promise.reject(error);
}
);
A 401 response commonly means that authentication failed
or the JWT token is no longer valid.
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
console.log("Authentication required");
}
return Promise.reject(error);
}
);
A 403 response generally means that the user is authenticated
but does not have permission to perform the requested action.
if (error.response?.status === 403) {
console.log("Access denied");
}
This is useful for role-based authorization.
The interceptor can inspect the HTTP status code.
api.interceptors.response.use(
response => {
console.log(response.status);
return response;
}
);
For example, a successful response may have status 200.
During development, a request interceptor can help us understand which API requests are being sent.
api.interceptors.request.use(config => {
console.log("Method:", config.method);
console.log("URL:", config.url);
return config;
});
Response interceptors can also log successful API responses.
api.interceptors.response.use(response => {
console.log("Status:", response.status);
return response;
});
Avoid logging sensitive information such as passwords or JWT tokens.
A common React Native architecture is:
This prevents us from manually adding the Authorization header to every request.
When the token must be loaded asynchronously, the request interceptor can be asynchronous.
api.interceptors.request.use(async config => {
const token = await getToken();
if (token) {
config.headers.Authorization =
`Bearer ${token}`;
}
return config;
});
A separate API service file keeps API configuration in one place.
// api.js
import axios from "axios";
const api = axios.create({
baseURL: "https://example.com/api"
});
export default api;
Other screens can import this instance instead of configuring Axios repeatedly.
After creating the Axios instance, we can use it for API requests.
const response = await api.get("/students");
console.log(response.data);
The request interceptor automatically runs before the request.
const response = await api.post(
"/students",
{
name: "Rahul",
email: "rahul@example.com"
}
);
console.log(response.data);
The same request interceptor can automatically add common headers and authentication.
import axios from "axios";
const api = axios.create({
baseURL: "https://example.com/api"
});
api.interceptors.request.use(
config => {
config.headers.Accept =
"application/json";
return config;
},
error => {
return Promise.reject(error);
}
);
api.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response?.status === 401) {
console.log("Please login again");
}
return Promise.reject(error);
}
);
export default api;
The complete flow looks like this:
React Native Screen
↓
Axios Request
↓
Request Interceptor
↓
PHP REST API
↓
PHP/MySQL
↓
API Response
↓
Response Interceptor
↓
React Native Screen
When a JWT expires, the PHP API may return 401 Unauthorized.
if (error.response?.status === 401) {
// Clear authentication state
// Navigate user to login screen
}
More advanced applications may use refresh tokens, which will be covered separately in an authentication section.
Never put database passwords, API private keys, or server secrets directly inside React Native source code.
// Do not do this
const secret = "MY_DATABASE_PASSWORD";
Mobile applications are distributed to users, so sensitive server credentials must remain on the server.
return config;
return response;
return Promise.reject(error);
Interceptors are useful for:
They help keep individual React Native screens cleaner.
A typical mobile application architecture can use Axios interceptors together with a PHP REST API.
React Native
↓
Axios Instance
↓
Request Interceptor
↓
Authorization: Bearer JWT
↓
PHP REST API
↓
JWT Verification
↓
MySQL
↓
JSON Response
↓
Response Interceptor
↓
React Native
api.js
import axios from "axios";
const api = axios.create({
baseURL: "https://example.com/api",
headers: {
Accept: "application/json"
}
});
api.interceptors.request.use(
async config => {
const token = await getToken();
if (token) {
config.headers.Authorization =
`Bearer ${token}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
api.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response?.status === 401) {
console.log("Session expired");
}
if (error.response?.status === 403) {
console.log("Access denied");
}
return Promise.reject(error);
}
);
export default api;
Using the API instance:
const loadStudents = async () => {
try {
const response = await api.get("/students");
console.log(response.data);
} catch (error) {
console.log("API request failed");
}
};
The request interceptor automatically prepares the request and the response interceptor centrally handles common responses and errors.
Question: Which Axios interceptor runs before a request is sent?