npm-axios

Description
Promise based HTTP client for the browser and node.js
Author
tessl
Last updated

How to use

npx @tessl/cli registry install tessl/npm-axios@1.11.0

interceptors.md docs/

1
# Interceptors
2
3
Intercept requests and responses to transform data, add authentication, handle errors, implement retry logic, or add logging before they are handled by `then` or `catch`.
4
5
## Capabilities
6
7
### Request Interceptors
8
9
Intercept requests before they are sent to the server. Useful for adding authentication tokens, logging, or modifying request data.
10
11
```javascript { .api }
12
/**
13
* Add a request interceptor
14
* @param onFulfilled - Function called for successful requests
15
* @param onRejected - Function called for request errors
16
* @param options - Additional interceptor options
17
* @returns Interceptor ID for later removal
18
*/
19
axios.interceptors.request.use(
20
onFulfilled?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig | Promise<InternalAxiosRequestConfig>,
21
onRejected?: (error: any) => any,
22
options?: AxiosInterceptorOptions
23
): number;
24
25
interface AxiosInterceptorOptions {
26
/** Execute interceptor synchronously */
27
synchronous?: boolean;
28
/** Conditionally run interceptor based on config */
29
runWhen?: (config: InternalAxiosRequestConfig) => boolean;
30
}
31
```
32
33
**Usage Examples:**
34
35
```javascript
36
// Add authentication token to all requests
37
axios.interceptors.request.use(
38
(config) => {
39
const token = localStorage.getItem("authToken");
40
if (token) {
41
config.headers.Authorization = `Bearer ${token}`;
42
}
43
return config;
44
},
45
(error) => {
46
console.error("Request error:", error);
47
return Promise.reject(error);
48
}
49
);
50
51
// Log all outgoing requests
52
axios.interceptors.request.use((config) => {
53
console.log(`Making ${config.method?.toUpperCase()} request to: ${config.url}`);
54
config.metadata = { startTime: Date.now() };
55
return config;
56
});
57
58
// Conditional interceptor
59
axios.interceptors.request.use(
60
(config) => {
61
// Add special header for admin endpoints
62
if (config.url?.includes("/admin/")) {
63
config.headers["X-Admin-Request"] = "true";
64
}
65
return config;
66
},
67
undefined,
68
{
69
runWhen: (config) => config.url?.includes("/admin/") || false
70
}
71
);
72
```
73
74
### Response Interceptors
75
76
Intercept responses before they are handled by the application. Useful for global error handling, data transformation, or caching.
77
78
```javascript { .api }
79
/**
80
* Add a response interceptor
81
* @param onFulfilled - Function called for successful responses
82
* @param onRejected - Function called for response errors
83
* @returns Interceptor ID for later removal
84
*/
85
axios.interceptors.response.use(
86
onFulfilled?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>,
87
onRejected?: (error: any) => any
88
): number;
89
```
90
91
**Usage Examples:**
92
93
```javascript
94
// Global error handling
95
axios.interceptors.response.use(
96
(response) => {
97
// Log successful responses
98
console.log(`Received ${response.status} from ${response.config.url}`);
99
return response;
100
},
101
(error) => {
102
if (error.response?.status === 401) {
103
// Redirect to login on unauthorized
104
window.location.href = "/login";
105
} else if (error.response?.status >= 500) {
106
// Show error notification for server errors
107
showErrorNotification("Server error occurred");
108
}
109
return Promise.reject(error);
110
}
111
);
112
113
// Data transformation
114
axios.interceptors.response.use((response) => {
115
// Transform snake_case API responses to camelCase
116
if (response.data && typeof response.data === "object") {
117
response.data = transformKeys(response.data, camelCase);
118
}
119
return response;
120
});
121
122
// Response timing
123
axios.interceptors.response.use(
124
(response) => {
125
const startTime = response.config.metadata?.startTime;
126
if (startTime) {
127
const duration = Date.now() - startTime;
128
console.log(`Request to ${response.config.url} took ${duration}ms`);
129
}
130
return response;
131
},
132
(error) => {
133
const startTime = error.config?.metadata?.startTime;
134
if (startTime) {
135
const duration = Date.now() - startTime;
136
console.log(`Failed request to ${error.config?.url} took ${duration}ms`);
137
}
138
return Promise.reject(error);
139
}
140
);
141
```
142
143
### Interceptor Management
144
145
Manage interceptors by removing them when no longer needed.
146
147
```javascript { .api }
148
/**
149
* Remove a request interceptor
150
* @param id - Interceptor ID returned from use()
151
*/
152
axios.interceptors.request.eject(id: number): void;
153
154
/**
155
* Remove a response interceptor
156
* @param id - Interceptor ID returned from use()
157
*/
158
axios.interceptors.response.eject(id: number): void;
159
160
/**
161
* Remove all request interceptors
162
*/
163
axios.interceptors.request.clear(): void;
164
165
/**
166
* Remove all response interceptors
167
*/
168
axios.interceptors.response.clear(): void;
169
```
170
171
**Usage Examples:**
172
173
```javascript
174
// Store interceptor ID for later removal
175
const requestInterceptorId = axios.interceptors.request.use((config) => {
176
// Add debugging header
177
config.headers["X-Debug"] = "true";
178
return config;
179
});
180
181
// Remove specific interceptor
182
axios.interceptors.request.eject(requestInterceptorId);
183
184
// Clear all interceptors
185
axios.interceptors.request.clear();
186
axios.interceptors.response.clear();
187
188
// Conditional cleanup
189
function setupInterceptors() {
190
if (process.env.NODE_ENV === "development") {
191
return axios.interceptors.request.use((config) => {
192
console.log("DEV REQUEST:", config);
193
return config;
194
});
195
}
196
return null;
197
}
198
199
function cleanupInterceptors(interceptorId) {
200
if (interceptorId !== null) {
201
axios.interceptors.request.eject(interceptorId);
202
}
203
}
204
```
205
206
### Interceptor Manager Interface
207
208
Each axios instance has its own interceptor managers for requests and responses.
209
210
```javascript { .api }
211
interface AxiosInterceptorManager<V> {
212
/** Add new interceptor */
213
use: V extends AxiosResponse
214
? AxiosResponseInterceptorUse<V>
215
: AxiosRequestInterceptorUse<V>;
216
/** Remove interceptor by ID */
217
eject(id: number): void;
218
/** Remove all interceptors */
219
clear(): void;
220
}
221
222
// Available on any axios instance
223
interface AxiosInstance {
224
interceptors: {
225
request: AxiosInterceptorManager<InternalAxiosRequestConfig>;
226
response: AxiosInterceptorManager<AxiosResponse>;
227
};
228
}
229
```
230
231
### Advanced Interceptor Patterns
232
233
Complex interceptor patterns for real-world applications.
234
235
**Usage Examples:**
236
237
```javascript
238
// Token refresh interceptor
239
let isRefreshing = false;
240
let refreshSubscribers = [];
241
242
axios.interceptors.response.use(
243
(response) => response,
244
async (error) => {
245
const originalRequest = error.config;
246
247
if (error.response?.status === 401 && !originalRequest._retry) {
248
if (isRefreshing) {
249
// Queue requests while refreshing
250
return new Promise((resolve) => {
251
refreshSubscribers.push((token) => {
252
originalRequest.headers.Authorization = `Bearer ${token}`;
253
resolve(axios(originalRequest));
254
});
255
});
256
}
257
258
originalRequest._retry = true;
259
isRefreshing = true;
260
261
try {
262
const refreshToken = localStorage.getItem("refreshToken");
263
const response = await axios.post("/auth/refresh", { refreshToken });
264
const newToken = response.data.accessToken;
265
266
localStorage.setItem("authToken", newToken);
267
268
// Retry queued requests
269
refreshSubscribers.forEach(callback => callback(newToken));
270
refreshSubscribers = [];
271
272
originalRequest.headers.Authorization = `Bearer ${newToken}`;
273
return axios(originalRequest);
274
} catch (refreshError) {
275
// Refresh failed, redirect to login
276
localStorage.clear();
277
window.location.href = "/login";
278
return Promise.reject(refreshError);
279
} finally {
280
isRefreshing = false;
281
}
282
}
283
284
return Promise.reject(error);
285
}
286
);
287
288
// Request/Response correlation
289
const requestMap = new Map();
290
291
axios.interceptors.request.use((config) => {
292
const requestId = Math.random().toString(36).substr(2, 9);
293
config.metadata = { requestId, startTime: Date.now() };
294
requestMap.set(requestId, config);
295
return config;
296
});
297
298
axios.interceptors.response.use(
299
(response) => {
300
const requestId = response.config.metadata?.requestId;
301
if (requestId) {
302
requestMap.delete(requestId);
303
const duration = Date.now() - response.config.metadata.startTime;
304
console.log(`✅ ${response.config.method?.toUpperCase()} ${response.config.url} (${duration}ms)`);
305
}
306
return response;
307
},
308
(error) => {
309
const requestId = error.config?.metadata?.requestId;
310
if (requestId) {
311
requestMap.delete(requestId);
312
const duration = Date.now() - error.config.metadata.startTime;
313
console.log(`❌ ${error.config?.method?.toUpperCase()} ${error.config?.url} (${duration}ms)`);
314
}
315
return Promise.reject(error);
316
}
317
);
318
319
// Retry with exponential backoff
320
axios.interceptors.response.use(
321
(response) => response,
322
async (error) => {
323
const config = error.config;
324
325
if (!config || !error.response || config.__retryCount >= 3) {
326
return Promise.reject(error);
327
}
328
329
// Retry only on network errors or 5xx status codes
330
if (error.response.status >= 500 || error.code === "NETWORK_ERROR") {
331
config.__retryCount = config.__retryCount || 0;
332
config.__retryCount += 1;
333
334
const delay = Math.pow(2, config.__retryCount) * 1000; // Exponential backoff
335
await new Promise(resolve => setTimeout(resolve, delay));
336
337
return axios(config);
338
}
339
340
return Promise.reject(error);
341
}
342
);
343
```