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.6.0

interceptors.md docs/

1
# Interceptors
2
3
Request and response middleware system for automatic processing, authentication, logging, error handling, and data transformation. Interceptors allow you to modify requests before they are sent and responses before they are handled.
4
5
## Capabilities
6
7
### Interceptor Manager Interface
8
9
Interface for managing request and response interceptors.
10
11
```typescript { .api }
12
interface AxiosInterceptorManager<V> {
13
/**
14
* Add interceptor
15
* @param onFulfilled - Success handler function
16
* @param onRejected - Error handler function
17
* @param options - Interceptor options
18
* @returns Interceptor ID for removal
19
*/
20
use(
21
onFulfilled?: ((value: V) => V | Promise<V>) | null,
22
onRejected?: ((error: any) => any) | null,
23
options?: AxiosInterceptorOptions
24
): number;
25
26
/**
27
* Remove interceptor by ID
28
* @param id - Interceptor ID returned by use()
29
*/
30
eject(id: number): void;
31
32
/**
33
* Remove all interceptors
34
*/
35
clear(): void;
36
}
37
38
interface AxiosInterceptorOptions {
39
/** Run interceptor synchronously */
40
synchronous?: boolean;
41
42
/** Conditional execution function */
43
runWhen?: (config: InternalAxiosRequestConfig) => boolean;
44
}
45
```
46
47
**Usage Examples:**
48
49
```typescript
50
import axios from "axios";
51
52
// Access interceptors from default instance
53
console.log(axios.interceptors.request); // Request interceptor manager
54
console.log(axios.interceptors.response); // Response interceptor manager
55
56
// Access interceptors from custom instance
57
const api = axios.create();
58
console.log(api.interceptors.request);
59
console.log(api.interceptors.response);
60
```
61
62
### Request Interceptors
63
64
Modify requests before they are sent to the server.
65
66
**Usage Examples:**
67
68
```typescript
69
import axios from "axios";
70
71
// Add authentication header
72
const authInterceptor = axios.interceptors.request.use(
73
(config) => {
74
const token = localStorage.getItem("authToken");
75
if (token) {
76
config.headers.Authorization = `Bearer ${token}`;
77
}
78
return config;
79
},
80
(error) => {
81
return Promise.reject(error);
82
}
83
);
84
85
// Add request logging
86
const loggingInterceptor = axios.interceptors.request.use(
87
(config) => {
88
console.log(`Making ${config.method?.toUpperCase()} request to ${config.url}`);
89
console.log("Request config:", config);
90
return config;
91
}
92
);
93
94
// Add request timing
95
const timingInterceptor = axios.interceptors.request.use(
96
(config) => {
97
config.metadata = { startTime: Date.now() };
98
return config;
99
}
100
);
101
102
// Add request ID for tracking
103
const requestIdInterceptor = axios.interceptors.request.use(
104
(config) => {
105
config.headers["X-Request-ID"] = `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
106
return config;
107
}
108
);
109
110
// Conditional interceptor
111
const conditionalInterceptor = axios.interceptors.request.use(
112
(config) => {
113
if (config.url?.includes("/api/v2/")) {
114
config.headers["API-Version"] = "2.0";
115
}
116
return config;
117
},
118
null,
119
{
120
runWhen: (config) => config.url?.includes("/api/") ?? false
121
}
122
);
123
124
// Handle request errors
125
const errorInterceptor = axios.interceptors.request.use(
126
(config) => config,
127
(error) => {
128
console.error("Request configuration error:", error);
129
// Could show user notification here
130
return Promise.reject(error);
131
}
132
);
133
```
134
135
### Response Interceptors
136
137
Process responses after they are received from the server.
138
139
**Usage Examples:**
140
141
```typescript
142
import axios from "axios";
143
144
// Transform response data
145
const dataTransformInterceptor = axios.interceptors.response.use(
146
(response) => {
147
// Transform dates from strings to Date objects
148
if (response.data && typeof response.data === "object") {
149
transformDates(response.data);
150
}
151
return response;
152
},
153
(error) => {
154
return Promise.reject(error);
155
}
156
);
157
158
// Add response timing
159
const responseTimingInterceptor = axios.interceptors.response.use(
160
(response) => {
161
const startTime = response.config.metadata?.startTime;
162
if (startTime) {
163
const duration = Date.now() - startTime;
164
console.log(`Request completed in ${duration}ms`);
165
}
166
return response;
167
}
168
);
169
170
// Response logging
171
const responseLoggingInterceptor = axios.interceptors.response.use(
172
(response) => {
173
console.log(`Response ${response.status} from ${response.config.url}`);
174
console.log("Response data:", response.data);
175
return response;
176
},
177
(error) => {
178
if (error.response) {
179
console.error(`Error ${error.response.status} from ${error.config?.url}`);
180
console.error("Error data:", error.response.data);
181
}
182
return Promise.reject(error);
183
}
184
);
185
186
// Automatic token refresh
187
const tokenRefreshInterceptor = axios.interceptors.response.use(
188
(response) => response,
189
async (error) => {
190
const originalRequest = error.config;
191
192
if (error.response?.status === 401 && !originalRequest._retry) {
193
originalRequest._retry = true;
194
195
try {
196
const refreshToken = localStorage.getItem("refreshToken");
197
const response = await axios.post("/auth/refresh", { refreshToken });
198
const newToken = response.data.accessToken;
199
200
localStorage.setItem("authToken", newToken);
201
originalRequest.headers.Authorization = `Bearer ${newToken}`;
202
203
return axios(originalRequest);
204
} catch (refreshError) {
205
// Refresh failed, redirect to login
206
localStorage.removeItem("authToken");
207
localStorage.removeItem("refreshToken");
208
window.location.href = "/login";
209
return Promise.reject(refreshError);
210
}
211
}
212
213
return Promise.reject(error);
214
}
215
);
216
217
// Error handling and user notifications
218
const errorHandlingInterceptor = axios.interceptors.response.use(
219
(response) => response,
220
(error) => {
221
if (error.response) {
222
const { status, data } = error.response;
223
224
switch (status) {
225
case 400:
226
showNotification("Invalid request data", "error");
227
break;
228
case 401:
229
showNotification("Authentication required", "warning");
230
break;
231
case 403:
232
showNotification("Access denied", "error");
233
break;
234
case 404:
235
showNotification("Resource not found", "warning");
236
break;
237
case 500:
238
showNotification("Server error occurred", "error");
239
break;
240
default:
241
showNotification(`Request failed: ${data.message || error.message}`, "error");
242
}
243
} else if (error.request) {
244
showNotification("Network error - please check your connection", "error");
245
}
246
247
return Promise.reject(error);
248
}
249
);
250
```
251
252
### Managing Interceptors
253
254
Add, remove, and configure interceptors dynamically.
255
256
**Usage Examples:**
257
258
```typescript
259
import axios from "axios";
260
261
// Store interceptor IDs for later removal
262
const interceptorIds: number[] = [];
263
264
// Add interceptors and store their IDs
265
interceptorIds.push(
266
axios.interceptors.request.use(config => {
267
console.log("Request interceptor 1");
268
return config;
269
})
270
);
271
272
interceptorIds.push(
273
axios.interceptors.request.use(config => {
274
console.log("Request interceptor 2");
275
return config;
276
})
277
);
278
279
interceptorIds.push(
280
axios.interceptors.response.use(response => {
281
console.log("Response interceptor 1");
282
return response;
283
})
284
);
285
286
// Remove specific interceptor
287
axios.interceptors.request.eject(interceptorIds[0]);
288
289
// Remove all request interceptors
290
axios.interceptors.request.clear();
291
292
// Remove all response interceptors
293
axios.interceptors.response.clear();
294
295
// Conditional interceptor management
296
class ApiClient {
297
private requestInterceptorId?: number;
298
private responseInterceptorId?: number;
299
300
enableAuthentication(token: string) {
301
// Remove existing auth interceptor if any
302
if (this.requestInterceptorId !== undefined) {
303
axios.interceptors.request.eject(this.requestInterceptorId);
304
}
305
306
// Add new auth interceptor
307
this.requestInterceptorId = axios.interceptors.request.use(
308
(config) => {
309
config.headers.Authorization = `Bearer ${token}`;
310
return config;
311
}
312
);
313
}
314
315
disableAuthentication() {
316
if (this.requestInterceptorId !== undefined) {
317
axios.interceptors.request.eject(this.requestInterceptorId);
318
this.requestInterceptorId = undefined;
319
}
320
}
321
322
enableLogging() {
323
this.responseInterceptorId = axios.interceptors.response.use(
324
(response) => {
325
console.log(`${response.status} ${response.config.method?.toUpperCase()} ${response.config.url}`);
326
return response;
327
}
328
);
329
}
330
331
disableLogging() {
332
if (this.responseInterceptorId !== undefined) {
333
axios.interceptors.response.eject(this.responseInterceptorId);
334
this.responseInterceptorId = undefined;
335
}
336
}
337
}
338
339
const client = new ApiClient();
340
client.enableAuthentication("token123");
341
client.enableLogging();
342
```
343
344
### Advanced Interceptor Patterns
345
346
Complex interceptor implementations for common use cases.
347
348
**Usage Examples:**
349
350
```typescript
351
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
352
353
// Request/response correlation
354
const correlationInterceptor = {
355
request: axios.interceptors.request.use((config) => {
356
const correlationId = `corr-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
357
config.headers["X-Correlation-ID"] = correlationId;
358
config.metadata = { ...config.metadata, correlationId };
359
return config;
360
}),
361
362
response: axios.interceptors.response.use(
363
(response) => {
364
const correlationId = response.config.metadata?.correlationId;
365
console.log(`Response received for correlation ID: ${correlationId}`);
366
return response;
367
},
368
(error) => {
369
const correlationId = error.config?.metadata?.correlationId;
370
console.error(`Error for correlation ID: ${correlationId}`, error);
371
return Promise.reject(error);
372
}
373
)
374
};
375
376
// Retry mechanism with exponential backoff
377
function createRetryInterceptor(maxRetries = 3, baseDelay = 1000) {
378
return axios.interceptors.response.use(
379
(response) => response,
380
async (error) => {
381
const config = error.config;
382
383
// Don't retry if we've exceeded max retries
384
if (config.__retryCount >= maxRetries) {
385
return Promise.reject(error);
386
}
387
388
// Don't retry client errors (4xx) except 429
389
if (error.response?.status >= 400 && error.response?.status < 500 && error.response?.status !== 429) {
390
return Promise.reject(error);
391
}
392
393
// Initialize retry count
394
config.__retryCount = config.__retryCount || 0;
395
config.__retryCount += 1;
396
397
// Calculate delay with exponential backoff
398
const delay = baseDelay * Math.pow(2, config.__retryCount - 1);
399
400
console.log(`Retrying request (attempt ${config.__retryCount}/${maxRetries}) after ${delay}ms delay`);
401
402
// Wait before retrying
403
await new Promise(resolve => setTimeout(resolve, delay));
404
405
// Retry the request
406
return axios(config);
407
}
408
);
409
}
410
411
// Enable retry interceptor
412
const retryInterceptorId = createRetryInterceptor(3, 1000);
413
414
// Caching interceptor
415
const cache = new Map<string, { data: any; timestamp: number; ttl: number }>();
416
417
function createCacheInterceptor(defaultTtl = 5 * 60 * 1000) { // 5 minutes
418
return {
419
request: axios.interceptors.request.use((config) => {
420
// Only cache GET requests
421
if (config.method?.toLowerCase() === "get") {
422
const cacheKey = `${config.method}:${config.url}:${JSON.stringify(config.params)}`;
423
const cached = cache.get(cacheKey);
424
425
if (cached && Date.now() - cached.timestamp < cached.ttl) {
426
console.log("Returning cached response for:", cacheKey);
427
// Return cached response wrapped in a resolved promise
428
config.__cached = true;
429
config.__cachedResponse = {
430
data: cached.data,
431
status: 200,
432
statusText: "OK (cached)",
433
headers: {},
434
config,
435
request: {}
436
};
437
}
438
}
439
return config;
440
}),
441
442
response: axios.interceptors.response.use((response) => {
443
// If this was a cached response, return it
444
if (response.config.__cached) {
445
return response.config.__cachedResponse;
446
}
447
448
// Cache GET responses
449
if (response.config.method?.toLowerCase() === "get" && response.status === 200) {
450
const cacheKey = `${response.config.method}:${response.config.url}:${JSON.stringify(response.config.params)}`;
451
const ttl = response.headers["cache-control"]?.includes("max-age=")
452
? parseInt(response.headers["cache-control"].match(/max-age=(\d+)/)?.[1] || "0") * 1000
453
: defaultTtl;
454
455
cache.set(cacheKey, {
456
data: response.data,
457
timestamp: Date.now(),
458
ttl
459
});
460
461
console.log("Cached response for:", cacheKey);
462
}
463
464
return response;
465
})
466
};
467
}
468
469
// Enable caching
470
const cacheInterceptor = createCacheInterceptor();
471
```
472
473
### Instance-Specific Interceptors
474
475
Use interceptors with specific axios instances for isolated behavior.
476
477
**Usage Examples:**
478
479
```typescript
480
import axios from "axios";
481
482
// Create specialized API clients
483
const apiV1 = axios.create({ baseURL: "https://api.example.com/v1" });
484
const apiV2 = axios.create({ baseURL: "https://api.example.com/v2" });
485
486
// Different auth for different APIs
487
apiV1.interceptors.request.use((config) => {
488
config.headers.Authorization = `Bearer ${getV1Token()}`;
489
return config;
490
});
491
492
apiV2.interceptors.request.use((config) => {
493
config.headers.Authorization = `Bearer ${getV2Token()}`;
494
config.headers["API-Version"] = "2.0";
495
return config;
496
});
497
498
// Different error handling for different APIs
499
apiV1.interceptors.response.use(
500
(response) => response,
501
(error) => {
502
console.log("API v1 error:", error);
503
return Promise.reject(error);
504
}
505
);
506
507
apiV2.interceptors.response.use(
508
(response) => response,
509
(error) => {
510
console.log("API v2 error:", error);
511
// V2 might have different error format
512
if (error.response?.data?.errors) {
513
error.message = error.response.data.errors.join(", ");
514
}
515
return Promise.reject(error);
516
}
517
);
518
519
// Use different instances
520
const v1Users = await apiV1.get("/users");
521
const v2Users = await apiV2.get("/users");
522
```