Implement Centralized Logging and Error Handling with Axios Interceptors
Learn how to add request and response interceptors to Axios for logging, injecting auth headers, and uniform error handling, then verify and clean them up safely.
20 Sept 2026, 13:50 UTC

Desired outcome
Configure Axios so every outgoing request is logged, an Authorization header is added when a token is available, and every response or error is logged uniformly. After verification, you can remove the interceptors without affecting other Axios instances.
Prerequisites
- Node.js >= 14 installed
- npm or yarn available to install packages
- A test HTTP endpoint that returns JSON (e.g.,
https://httpbin.org/get) or a local mock server - Basic familiarity with JavaScript ES6 syntax
Install Axios
In your project folder run:
npm install axios
# or
yarn add axios
Procedure
- Create a test script
Create a file named
test-interceptor.jsin your project root.const axios = require('axios'); // Replace with your actual token or leave undefined for none const token = process.env.TOKEN || 'dummy-token-for-demo'; // Request interceptor: log and inject auth header const requestId = axios.interceptors.request.use((config) => { console.log(`[REQUEST] ${config.method.toUpperCase()} ${config.url}`); if (token) { // Shallow copy to avoid mutating the shared config object const newConfig = { ...config }; newConfig.headers.Authorization = `Bearer ${token}`; return newConfig; } return config; }, (error) => { // Pass through request errors return Promise.reject(error); }); // Response interceptor: log responses and errors const responseId = axios.interceptors.response.use((response) => { console.log(`[RESPONSE] ${response.status} ${JSON.stringify(response.data).substring(0, 100)}...`); return response; }, (error) => { if (error.response) { console.error(`[ERROR RESPONSE] ${error.response.status} ${JSON.stringify(error.response.data)}`); } else if (error.request) { console.error('[ERROR REQUEST] No response received:', error.message); } else { console.error('[ERROR SETUP]', error.message); } return Promise.reject(error); }); // Perform a sample GET request axios.get('https://httpbin.org/get') .then((res) => { console.log('Request succeeded'); }) .catch((err) => { console.log('Request failed as expected'); }); // Store interceptor IDs for later removal module.exports = { requestId, responseId }; - Run the script and observe logs
Execute:
node test-interceptor.jsYou should see output similar to:
[REQUEST] GET https://httpbin.org/get[RESPONSE] 200 {...}- Optional error lines if the request fails
The Authorization header will be visible in the request log if you inspect the
configobject (you can addconsole.log(config.headers)inside the interceptor for debugging). - Remove the interceptors
Create a second script to verify that ejecting stops logging:
const axios = require('axios'); const { requestId, responseId } = require('./test-interceptor'); // Eject the previously added interceptors axios.interceptors.request.eject(requestId); axios.interceptors.response.eject(responseId); // Same request again – no interceptor output should appear axios.get('https://httpbin.org/get') .then(() => console.log('Request completed (no interceptor logs)')) .catch(() => console.log('Request failed (no interceptor logs)'));Run it:
node test-eject.jsCheck that no
[REQUEST],[RESPONSE], or[ERROR]lines appear in the console. - Optional: Use a mock for offline testing
If you prefer not to rely on an external service, install
nockand mock the endpoint:npm install nockThen replace the GET line with:
const nock = require('nock'); nock('https://httpbin.org') .get('/get') .reply(200, { args: {} }); axios.get('https://httpbin.org/get') // … same handling …This validates interceptor execution without network traffic.
Expected checks
- Request logs show the HTTP method, URL, and (when a token is present) an Authorization header.
- Response logs show the status code and a truncated payload.
- Error logs distinguish between response errors, request errors, and setup errors.
- After calling
eject()with the stored IDs, subsequent requests produce no interceptor‑generated console output.
Recovery options
If you accidentally leave interceptors attached in a long‑lived process (e.g., a server), they will continue to run for every Axios call, potentially causing duplicate logs or header duplication. To recover:
- Identify where the interceptors were registered (usually a central setup file).
- Call
axios.interceptors.request.eject(id)andaxios.interceptors.response.eject(id)for each stored ID. - If IDs were not saved, you can remove all interceptors by reassigning empty arrays:
axios.interceptors.request = [];
axios.interceptors.response = [];
Note: This also removes any other interceptors you may have added elsewhere, so use it only when you control the entire Axios instance.
Limitations and cautions
- Avoid heavy synchronous work (e.g., large JSON parsing) inside interceptors; it adds latency to every request.
- Do not mutate the shared
configobject directly; always create a shallow copy before changing headers to prevent side‑effects across concurrent requests. - Remember to eject interceptors in cleanup hooks (e.g.,
process.exitlisteners or server shutdown) to avoid memory leaks. - If you use multiple Axios instances (e.g.,
axios.create()), interceptors must be added to each instance separately unless you use the defaultaxiossingleton.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.