@rutansh0101/fetchify 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -1091
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,911 +4,188 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@rutansh0101/fetchify)
|
|
5
5
|
[](https://github.com/Rutansh0101/Fetchify-Rutansh-Chawla/blob/master/LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A modern, lightweight HTTP client library built on top of the native Fetch API. It provides a clean, axios-like interface with support for interceptors, request/response transformation, and timeout handling.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
1. [Installation & Setup](#installation--setup)
|
|
14
|
-
2. [Basic Usage](#basic-usage)
|
|
15
|
-
3. [Features Overview](#features-overview)
|
|
16
|
-
4. [Detailed Feature Explanation](#detailed-feature-explanation)
|
|
17
|
-
- [Instance Creation](#1-instance-creation)
|
|
18
|
-
- [HTTP Methods](#2-http-methods)
|
|
19
|
-
- [Configuration Management](#3-configuration-management)
|
|
20
|
-
- [Timeout Handling](#4-timeout-handling)
|
|
21
|
-
- [Request Interceptors](#5-request-interceptors)
|
|
22
|
-
- [Response Interceptors](#6-response-interceptors)
|
|
23
|
-
- [Interceptor Chain Execution](#7-interceptor-chain-execution)
|
|
24
|
-
5. [Architecture & Design Decisions](#architecture--design-decisions)
|
|
25
|
-
6. [Code Examples](#code-examples)
|
|
26
|
-
7. [Error Handling](#error-handling)
|
|
27
|
-
8. [API Reference](#api-reference)
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Installation & Setup
|
|
32
|
-
|
|
33
|
-
### Installation
|
|
11
|
+
## Installation
|
|
34
12
|
|
|
35
13
|
```bash
|
|
36
14
|
npm install @rutansh0101/fetchify
|
|
37
15
|
```
|
|
38
16
|
|
|
39
|
-
|
|
40
|
-
- Node.js 14+ or modern browser with Fetch API support
|
|
41
|
-
- ES6+ module support
|
|
42
|
-
|
|
43
|
-
### Import
|
|
17
|
+
## Quick Start
|
|
44
18
|
|
|
45
19
|
```javascript
|
|
46
20
|
import fetchify from '@rutansh0101/fetchify';
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
---
|
|
50
21
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
### Creating an Instance
|
|
54
|
-
|
|
55
|
-
```javascript
|
|
56
|
-
import fetchify from '@rutansh0101/fetchify';
|
|
57
|
-
|
|
58
|
-
// Create an instance with base configuration
|
|
22
|
+
// Create an instance
|
|
59
23
|
const api = fetchify.create({
|
|
60
24
|
baseURL: 'https://api.example.com',
|
|
61
25
|
timeout: 5000,
|
|
62
26
|
headers: {
|
|
63
|
-
'Content-Type': 'application/json'
|
|
64
|
-
'Authorization': 'Bearer YOUR_TOKEN'
|
|
27
|
+
'Content-Type': 'application/json'
|
|
65
28
|
}
|
|
66
29
|
});
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Making Requests
|
|
70
30
|
|
|
71
|
-
|
|
72
|
-
// GET request
|
|
31
|
+
// Make requests
|
|
73
32
|
const response = await api.get('/users');
|
|
74
33
|
const users = await response.json();
|
|
75
|
-
|
|
76
|
-
// POST request
|
|
77
|
-
const newUser = await api.post('/users', {
|
|
78
|
-
body: JSON.stringify({ name: 'John', email: 'john@example.com' })
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// PUT request
|
|
82
|
-
const updated = await api.put('/users/1', {
|
|
83
|
-
body: JSON.stringify({ name: 'John Updated' })
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// PATCH request
|
|
87
|
-
const patched = await api.patch('/users/1', {
|
|
88
|
-
body: JSON.stringify({ email: 'newemail@example.com' })
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// DELETE request
|
|
92
|
-
await api.delete('/users/1');
|
|
93
34
|
```
|
|
94
35
|
|
|
95
36
|
---
|
|
96
37
|
|
|
97
|
-
## Features
|
|
38
|
+
## Features
|
|
98
39
|
|
|
99
|
-
✅ **Axios-like API** - Familiar
|
|
100
|
-
✅ **Request/Response Interceptors** - Transform requests and responses
|
|
40
|
+
✅ **Axios-like API** - Familiar and easy to use
|
|
41
|
+
✅ **Request/Response Interceptors** - Transform requests and responses
|
|
101
42
|
✅ **Timeout Support** - Abort requests after specified duration
|
|
102
43
|
✅ **Configuration Merging** - Instance, request-level, and default configs
|
|
103
|
-
✅ **All HTTP Methods** - GET, POST, PUT, PATCH, DELETE
|
|
104
|
-
✅ **
|
|
105
|
-
✅ **Promise Chain Architecture** - Clean async operation handling
|
|
106
|
-
✅ **Error Transformation** - Custom error messages for timeouts and failures
|
|
107
|
-
|
|
108
|
-
---
|
|
109
|
-
|
|
110
|
-
## Detailed Feature Explanation
|
|
111
|
-
|
|
112
|
-
### 1. Instance Creation
|
|
113
|
-
|
|
114
|
-
**What it does:**
|
|
115
|
-
Creates a reusable HTTP client instance with shared configuration.
|
|
116
|
-
|
|
117
|
-
**Why it's needed:**
|
|
118
|
-
- Avoid repeating baseURL, headers, and timeout in every request
|
|
119
|
-
- Create multiple instances for different APIs (e.g., one for auth, one for data)
|
|
120
|
-
- Centralize common configuration
|
|
121
|
-
|
|
122
|
-
**How it works:**
|
|
123
|
-
|
|
124
|
-
```javascript
|
|
125
|
-
const create = (config) => {
|
|
126
|
-
return new Fetchify(config);
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
The `create` method:
|
|
131
|
-
1. Accepts a configuration object
|
|
132
|
-
2. Instantiates the Fetchify class
|
|
133
|
-
3. Merges user config with default config
|
|
134
|
-
4. Returns the configured instance
|
|
135
|
-
|
|
136
|
-
**Implementation Details:**
|
|
137
|
-
|
|
138
|
-
```javascript
|
|
139
|
-
constructor(newConfig) {
|
|
140
|
-
this.config = this.#mergeConfig(newConfig);
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
The constructor:
|
|
145
|
-
- Calls `#mergeConfig` to combine default config with user-provided config
|
|
146
|
-
- Stores the merged config in `this.config`
|
|
147
|
-
- This config becomes the base for all future requests
|
|
148
|
-
|
|
149
|
-
**Default Configuration:**
|
|
150
|
-
|
|
151
|
-
```javascript
|
|
152
|
-
config = {
|
|
153
|
-
headers: {
|
|
154
|
-
'Content-Type': 'application/json'
|
|
155
|
-
},
|
|
156
|
-
timeout: 1000 // 1 second default timeout
|
|
157
|
-
};
|
|
158
|
-
```
|
|
44
|
+
✅ **All HTTP Methods** - GET, POST, PUT, PATCH, DELETE
|
|
45
|
+
✅ **Lightweight** - ~5KB with zero dependencies
|
|
159
46
|
|
|
160
47
|
---
|
|
161
48
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
**What it does:**
|
|
165
|
-
Provides convenient methods for different HTTP verbs (GET, POST, PUT, PATCH, DELETE).
|
|
166
|
-
|
|
167
|
-
**Why it's needed:**
|
|
168
|
-
- Simplifies request syntax
|
|
169
|
-
- Makes code more readable (`api.get()` vs `api.request({ method: 'GET' })`)
|
|
170
|
-
- Follows REST conventions
|
|
49
|
+
## Basic Usage
|
|
171
50
|
|
|
172
|
-
|
|
51
|
+
### Making Requests
|
|
173
52
|
|
|
174
53
|
```javascript
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
config: { ...tempConfig, method: 'GET' }
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Each method:
|
|
184
|
-
1. Takes an endpoint and optional temporary config
|
|
185
|
-
2. Merges the HTTP method into the config
|
|
186
|
-
3. Calls the private `#request` method
|
|
187
|
-
4. Returns a Promise that resolves to the fetch Response object
|
|
188
|
-
|
|
189
|
-
**All Methods:**
|
|
190
|
-
|
|
191
|
-
- **GET**: Retrieve data (no body allowed by HTTP spec)
|
|
192
|
-
- **POST**: Create new resources (requires body)
|
|
193
|
-
- **PUT**: Update entire resources (requires body)
|
|
194
|
-
- **PATCH**: Partially update resources (requires body)
|
|
195
|
-
- **DELETE**: Remove resources (usually no body)
|
|
196
|
-
|
|
197
|
-
**Example with Body:**
|
|
54
|
+
// GET request
|
|
55
|
+
const response = await api.get('/users');
|
|
56
|
+
const data = await response.json();
|
|
198
57
|
|
|
199
|
-
|
|
58
|
+
// POST request
|
|
200
59
|
await api.post('/users', {
|
|
201
|
-
body: JSON.stringify({ name: 'John' })
|
|
202
|
-
headers: { 'Custom-Header': 'value' }
|
|
60
|
+
body: JSON.stringify({ name: 'John', email: 'john@example.com' })
|
|
203
61
|
});
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
### 3. Configuration Management
|
|
209
|
-
|
|
210
|
-
**What it does:**
|
|
211
|
-
Handles merging of configurations at three levels: default, instance, and request.
|
|
212
|
-
|
|
213
|
-
**Why it's needed:**
|
|
214
|
-
- Allow global defaults that can be overridden
|
|
215
|
-
- Support instance-specific configs (different baseURLs)
|
|
216
|
-
- Enable request-specific overrides (custom timeout for one request)
|
|
217
|
-
|
|
218
|
-
**Configuration Priority (highest to lowest):**
|
|
219
|
-
1. Request-level config (passed to `get()`, `post()`, etc.)
|
|
220
|
-
2. Instance-level config (passed to `create()`)
|
|
221
|
-
3. Default config (hardcoded in the class)
|
|
222
|
-
|
|
223
|
-
**How it works:**
|
|
224
|
-
|
|
225
|
-
```javascript
|
|
226
|
-
#mergeConfig(newConfig) {
|
|
227
|
-
return {
|
|
228
|
-
...this.config, // Existing config
|
|
229
|
-
...newConfig, // New config (overrides existing)
|
|
230
|
-
headers: { // Deep merge for headers
|
|
231
|
-
...this.config.headers,
|
|
232
|
-
...newConfig?.headers
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
**Why Deep Merge for Headers?**
|
|
239
62
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// Request config
|
|
246
|
-
headers: { 'X-Custom': 'value' }
|
|
247
|
-
|
|
248
|
-
// Result WITHOUT deep merge (WRONG):
|
|
249
|
-
headers: { 'X-Custom': 'value' } // Lost Authorization!
|
|
250
|
-
|
|
251
|
-
// Result WITH deep merge (CORRECT):
|
|
252
|
-
headers: {
|
|
253
|
-
'Authorization': 'Bearer token',
|
|
254
|
-
'Content-Type': 'application/json',
|
|
255
|
-
'X-Custom': 'value'
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
**Two Merge Methods:**
|
|
260
|
-
|
|
261
|
-
1. `#mergeConfig(newConfig)` - Merges with instance config (`this.config`)
|
|
262
|
-
2. `#mergeConfigs(config1, config2)` - Merges two arbitrary configs
|
|
263
|
-
|
|
264
|
-
---
|
|
265
|
-
|
|
266
|
-
### 4. Timeout Handling
|
|
267
|
-
|
|
268
|
-
**What it does:**
|
|
269
|
-
Automatically aborts requests that take longer than specified duration.
|
|
270
|
-
|
|
271
|
-
**Why it's needed:**
|
|
272
|
-
- Prevent requests from hanging indefinitely
|
|
273
|
-
- Improve user experience with faster feedback
|
|
274
|
-
- Handle slow/unresponsive servers gracefully
|
|
275
|
-
- Free up resources from stalled connections
|
|
276
|
-
|
|
277
|
-
**How it works:**
|
|
278
|
-
|
|
279
|
-
```javascript
|
|
280
|
-
// 1. Create AbortController
|
|
281
|
-
const abortController = new AbortController();
|
|
282
|
-
const timeout = config.timeout || this.config.timeout || 0;
|
|
283
|
-
|
|
284
|
-
// 2. Set timer to abort
|
|
285
|
-
let timeOutId;
|
|
286
|
-
if (timeout) {
|
|
287
|
-
timeOutId = setTimeout(() => {
|
|
288
|
-
abortController.abort(); // Trigger abort after timeout
|
|
289
|
-
}, timeout);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// 3. Link signal to fetch
|
|
293
|
-
config.signal = abortController.signal;
|
|
294
|
-
|
|
295
|
-
// 4. Execute fetch
|
|
296
|
-
const response = await fetch(url, config);
|
|
297
|
-
|
|
298
|
-
// 5. Clear timer in finally block
|
|
299
|
-
finally {
|
|
300
|
-
if (timeOutId) {
|
|
301
|
-
clearTimeout(timeOutId); // Prevent memory leak
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
**Why Use AbortController?**
|
|
307
|
-
|
|
308
|
-
- Native browser API for cancelling fetch requests
|
|
309
|
-
- Cleaner than old XMLHttpRequest cancellation
|
|
310
|
-
- Works across all modern browsers
|
|
311
|
-
- Can abort multiple operations with one controller
|
|
63
|
+
// PUT request
|
|
64
|
+
await api.put('/users/1', {
|
|
65
|
+
body: JSON.stringify({ name: 'Jane Doe' })
|
|
66
|
+
});
|
|
312
67
|
|
|
313
|
-
|
|
68
|
+
// PATCH request
|
|
69
|
+
await api.patch('/users/1', {
|
|
70
|
+
body: JSON.stringify({ email: 'jane@example.com' })
|
|
71
|
+
});
|
|
314
72
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
↓
|
|
318
|
-
Set Timeout Timer (e.g., 5000ms)
|
|
319
|
-
↓
|
|
320
|
-
Start Fetch Request
|
|
321
|
-
↓
|
|
322
|
-
├─→ Response arrives in 2000ms → Clear Timer → Return Response ✓
|
|
323
|
-
│
|
|
324
|
-
└─→ 5000ms passes → Timer fires → Abort Signal → Fetch throws AbortError ✗
|
|
73
|
+
// DELETE request
|
|
74
|
+
await api.delete('/users/1');
|
|
325
75
|
```
|
|
326
76
|
|
|
327
|
-
|
|
77
|
+
### Request Configuration
|
|
328
78
|
|
|
329
|
-
|
|
330
|
-
// Without clearing:
|
|
331
|
-
setTimeout(() => abortController.abort(), 5000);
|
|
332
|
-
// If request finishes in 2s, timer still exists in memory until 5s
|
|
333
|
-
// With 1000 requests, you have 1000 timers lingering!
|
|
334
|
-
|
|
335
|
-
// With clearing:
|
|
336
|
-
finally {
|
|
337
|
-
clearTimeout(timeOutId); // Immediately free memory when done
|
|
338
|
-
}
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
**Error Handling:**
|
|
79
|
+
Override default settings per request:
|
|
342
80
|
|
|
343
81
|
```javascript
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
throw error;
|
|
82
|
+
const response = await api.get('/users', {
|
|
83
|
+
timeout: 10000,
|
|
84
|
+
headers: {
|
|
85
|
+
'Authorization': 'Bearer token123'
|
|
349
86
|
}
|
|
350
|
-
}
|
|
87
|
+
});
|
|
351
88
|
```
|
|
352
89
|
|
|
353
|
-
The code checks `error.name === 'AbortError'` to distinguish timeout aborts from other errors.
|
|
354
|
-
|
|
355
90
|
---
|
|
356
91
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
**What it does:**
|
|
360
|
-
Intercepts and modifies requests before they are sent to the server.
|
|
92
|
+
## Interceptors
|
|
361
93
|
|
|
362
|
-
|
|
363
|
-
- Add authentication tokens to all requests
|
|
364
|
-
- Log outgoing requests for debugging
|
|
365
|
-
- Modify request data (transform, encrypt, compress)
|
|
366
|
-
- Add timestamps or request IDs
|
|
367
|
-
- Implement retry logic
|
|
94
|
+
### Request Interceptors
|
|
368
95
|
|
|
369
|
-
|
|
96
|
+
Modify requests before they are sent:
|
|
370
97
|
|
|
371
98
|
```javascript
|
|
372
|
-
// Adding an interceptor
|
|
373
99
|
api.addRequestInterceptor(
|
|
374
100
|
(config) => {
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
config.config.headers['
|
|
378
|
-
return config;
|
|
379
|
-
},
|
|
380
|
-
(error) => {
|
|
381
|
-
// Error handler: runs if previous interceptor failed
|
|
382
|
-
console.error('Request interceptor error:', error);
|
|
383
|
-
return Promise.reject(error);
|
|
384
|
-
}
|
|
385
|
-
);
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
**Storage:**
|
|
389
|
-
|
|
390
|
-
```javascript
|
|
391
|
-
requestInterceptors = []; // Array of { successHandler, errorHandler }
|
|
392
|
-
|
|
393
|
-
addRequestInterceptor(successHandler, errorHandler) {
|
|
394
|
-
this.requestInterceptors.push({ successHandler, errorHandler });
|
|
395
|
-
}
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
**Execution in Chain:**
|
|
399
|
-
|
|
400
|
-
```javascript
|
|
401
|
-
const chain = [
|
|
402
|
-
...this.requestInterceptors, // All request interceptors
|
|
403
|
-
{ successHandler: this.#dispatchRequest.bind(this) }, // Actual fetch
|
|
404
|
-
...this.responseInterceptors // All response interceptors
|
|
405
|
-
];
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
Request interceptors run **before** the actual fetch call.
|
|
409
|
-
|
|
410
|
-
**Input/Output Format:**
|
|
411
|
-
|
|
412
|
-
- **Input**: `{ endPoint: '/users', config: { method: 'GET', ... } }`
|
|
413
|
-
- **Output**: Must return same format (modified or not)
|
|
414
|
-
|
|
415
|
-
**Common Use Cases:**
|
|
416
|
-
|
|
417
|
-
```javascript
|
|
418
|
-
// 1. Add authentication
|
|
419
|
-
api.addRequestInterceptor((config) => {
|
|
420
|
-
const token = localStorage.getItem('token');
|
|
421
|
-
config.config.headers['Authorization'] = `Bearer ${token}`;
|
|
422
|
-
return config;
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// 2. Log requests
|
|
426
|
-
api.addRequestInterceptor((config) => {
|
|
427
|
-
console.log(`[${config.config.method}] ${config.endPoint}`);
|
|
428
|
-
return config;
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
// 3. Transform request body
|
|
432
|
-
api.addRequestInterceptor((config) => {
|
|
433
|
-
if (config.config.body) {
|
|
434
|
-
// Add timestamp to all POST requests
|
|
435
|
-
const body = JSON.parse(config.config.body);
|
|
436
|
-
body.timestamp = Date.now();
|
|
437
|
-
config.config.body = JSON.stringify(body);
|
|
438
|
-
}
|
|
439
|
-
return config;
|
|
440
|
-
});
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
|
|
445
|
-
### 6. Response Interceptors
|
|
446
|
-
|
|
447
|
-
**What it does:**
|
|
448
|
-
Intercepts and modifies responses before they reach your application code.
|
|
449
|
-
|
|
450
|
-
**Why it's needed:**
|
|
451
|
-
- Transform response data globally
|
|
452
|
-
- Handle errors consistently (e.g., redirect on 401)
|
|
453
|
-
- Log responses for debugging
|
|
454
|
-
- Extract data from nested response structures
|
|
455
|
-
- Implement global retry logic
|
|
456
|
-
|
|
457
|
-
**How it works:**
|
|
458
|
-
|
|
459
|
-
```javascript
|
|
460
|
-
// Adding an interceptor
|
|
461
|
-
api.addResponseInterceptor(
|
|
462
|
-
(response) => {
|
|
463
|
-
// Success handler: runs for successful responses
|
|
464
|
-
console.log('Response status:', response.status);
|
|
465
|
-
|
|
466
|
-
if (!response.ok) {
|
|
467
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return response; // Must return response
|
|
101
|
+
// Add authentication token
|
|
102
|
+
const token = localStorage.getItem('authToken');
|
|
103
|
+
config.config.headers['Authorization'] = `Bearer ${token}`;
|
|
104
|
+
return config;
|
|
471
105
|
},
|
|
472
106
|
(error) => {
|
|
473
|
-
// Error handler: runs on fetch errors or if previous interceptor threw
|
|
474
|
-
console.error('Response error:', error);
|
|
475
|
-
|
|
476
|
-
if (error.message.includes('timeout')) {
|
|
477
|
-
alert('Request timed out. Please try again.');
|
|
478
|
-
}
|
|
479
|
-
|
|
480
107
|
return Promise.reject(error);
|
|
481
108
|
}
|
|
482
109
|
);
|
|
483
110
|
```
|
|
484
111
|
|
|
485
|
-
|
|
112
|
+
### Response Interceptors
|
|
486
113
|
|
|
487
|
-
|
|
488
|
-
responseInterceptors = []; // Array of { successHandler, errorHandler }
|
|
489
|
-
|
|
490
|
-
addResponseInterceptor(successHandler, errorHandler) {
|
|
491
|
-
this.responseInterceptors.push({ successHandler, errorHandler });
|
|
492
|
-
}
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
**Execution in Chain:**
|
|
496
|
-
|
|
497
|
-
Response interceptors run **after** the fetch completes.
|
|
498
|
-
|
|
499
|
-
**Input/Output Format:**
|
|
500
|
-
|
|
501
|
-
- **Input**: Native fetch `Response` object
|
|
502
|
-
- **Output**: Must return `Response` object (or modified version)
|
|
503
|
-
|
|
504
|
-
**Common Use Cases:**
|
|
114
|
+
Handle responses globally:
|
|
505
115
|
|
|
506
116
|
```javascript
|
|
507
|
-
// 1. Auto-logout on 401
|
|
508
117
|
api.addResponseInterceptor(
|
|
509
118
|
(response) => {
|
|
119
|
+
// Handle unauthorized responses
|
|
510
120
|
if (response.status === 401) {
|
|
511
|
-
localStorage.removeItem('token');
|
|
512
121
|
window.location.href = '/login';
|
|
513
122
|
}
|
|
514
123
|
return response;
|
|
515
|
-
}
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
// 2. Parse all responses as JSON
|
|
519
|
-
api.addResponseInterceptor(
|
|
520
|
-
async (response) => {
|
|
521
|
-
const data = await response.json();
|
|
522
|
-
// Return modified response with parsed data
|
|
523
|
-
response.data = data;
|
|
524
|
-
return response;
|
|
525
|
-
}
|
|
526
|
-
);
|
|
527
|
-
|
|
528
|
-
// 3. Handle rate limiting
|
|
529
|
-
api.addResponseInterceptor(
|
|
530
|
-
(response) => {
|
|
531
|
-
if (response.status === 429) {
|
|
532
|
-
const retryAfter = response.headers.get('Retry-After');
|
|
533
|
-
throw new Error(`Rate limited. Retry after ${retryAfter} seconds`);
|
|
534
|
-
}
|
|
535
|
-
return response;
|
|
536
|
-
}
|
|
537
|
-
);
|
|
538
|
-
|
|
539
|
-
// 4. Log all responses
|
|
540
|
-
api.addResponseInterceptor(
|
|
541
|
-
(response) => {
|
|
542
|
-
console.log(`[${response.status}] Response received`);
|
|
543
|
-
return response;
|
|
544
|
-
}
|
|
545
|
-
);
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
---
|
|
549
|
-
|
|
550
|
-
### 7. Interceptor Chain Execution
|
|
551
|
-
|
|
552
|
-
**What it does:**
|
|
553
|
-
Executes all interceptors and the fetch request in a sequential promise chain.
|
|
554
|
-
|
|
555
|
-
**Why it's needed:**
|
|
556
|
-
- Ensure interceptors run in order (request → fetch → response)
|
|
557
|
-
- Handle errors at any stage
|
|
558
|
-
- Allow each interceptor to modify data for the next
|
|
559
|
-
- Maintain clean async flow
|
|
560
|
-
|
|
561
|
-
**The Chain Structure:**
|
|
562
|
-
|
|
563
|
-
```javascript
|
|
564
|
-
const chain = [
|
|
565
|
-
...this.requestInterceptors, // [interceptor1, interceptor2, ...]
|
|
566
|
-
{ successHandler: this.#dispatchRequest.bind(this) }, // The actual fetch
|
|
567
|
-
...this.responseInterceptors // [interceptor3, interceptor4, ...]
|
|
568
|
-
];
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
**Visual Representation:**
|
|
572
|
-
|
|
573
|
-
```
|
|
574
|
-
Initial Promise.resolve({ endPoint, config })
|
|
575
|
-
↓
|
|
576
|
-
Request Interceptor 1 (success/error)
|
|
577
|
-
↓
|
|
578
|
-
Request Interceptor 2 (success/error)
|
|
579
|
-
↓
|
|
580
|
-
#dispatchRequest (actual fetch)
|
|
581
|
-
↓
|
|
582
|
-
Response Interceptor 1 (success/error)
|
|
583
|
-
↓
|
|
584
|
-
Response Interceptor 2 (success/error)
|
|
585
|
-
↓
|
|
586
|
-
Final Result returned to user
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
**Chain Building Code:**
|
|
590
|
-
|
|
591
|
-
```javascript
|
|
592
|
-
let promise = Promise.resolve({ endPoint, config: finalConfig });
|
|
593
|
-
|
|
594
|
-
for (const { successHandler, errorHandler } of chain) {
|
|
595
|
-
promise = promise.then(
|
|
596
|
-
(responseOfPrevPromise) => {
|
|
597
|
-
try {
|
|
598
|
-
return successHandler(responseOfPrevPromise);
|
|
599
|
-
} catch (error) {
|
|
600
|
-
if (errorHandler) {
|
|
601
|
-
return errorHandler(error);
|
|
602
|
-
} else {
|
|
603
|
-
return Promise.reject(error);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
},
|
|
607
|
-
(error) => {
|
|
608
|
-
if (errorHandler) {
|
|
609
|
-
return errorHandler(error);
|
|
610
|
-
} else {
|
|
611
|
-
return Promise.reject(error);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
return promise;
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
**How It Works Step-by-Step:**
|
|
621
|
-
|
|
622
|
-
1. **Initial Promise**: Starts with `{ endPoint, config }`
|
|
623
|
-
|
|
624
|
-
2. **Iteration**: For each interceptor in the chain:
|
|
625
|
-
- Attach `.then()` to the promise
|
|
626
|
-
- Pass output of previous step as input to next
|
|
627
|
-
|
|
628
|
-
3. **Success Path**: `then(successHandler, errorHandler)`
|
|
629
|
-
- If previous promise resolved → `successHandler` runs
|
|
630
|
-
- Output becomes input for next interceptor
|
|
631
|
-
|
|
632
|
-
4. **Error Path**: Second parameter of `.then()`
|
|
633
|
-
- If previous promise rejected → `errorHandler` runs
|
|
634
|
-
- Can recover (return value) or propagate (reject)
|
|
635
|
-
|
|
636
|
-
5. **Try-Catch in Success Handler**:
|
|
637
|
-
- If `successHandler` throws synchronous error
|
|
638
|
-
- Catch it and pass to `errorHandler`
|
|
639
|
-
- This handles errors that don't return rejected promises
|
|
640
|
-
|
|
641
|
-
**Example Flow:**
|
|
642
|
-
|
|
643
|
-
```javascript
|
|
644
|
-
// User code
|
|
645
|
-
const response = await api.get('/users');
|
|
646
|
-
|
|
647
|
-
// What happens:
|
|
648
|
-
|
|
649
|
-
// Step 1: Initial promise
|
|
650
|
-
Promise.resolve({ endPoint: '/users', config: { method: 'GET' } })
|
|
651
|
-
|
|
652
|
-
// Step 2: Request Interceptor 1
|
|
653
|
-
.then(config => {
|
|
654
|
-
config.config.headers['Auth'] = 'token';
|
|
655
|
-
return config; // { endPoint: '/users', config: { method: 'GET', headers: {...} } }
|
|
656
|
-
})
|
|
657
|
-
|
|
658
|
-
// Step 3: Request Interceptor 2
|
|
659
|
-
.then(config => {
|
|
660
|
-
console.log('Logging request');
|
|
661
|
-
return config; // Same object passed through
|
|
662
|
-
})
|
|
663
|
-
|
|
664
|
-
// Step 4: Dispatch Request (actual fetch)
|
|
665
|
-
.then(async config => {
|
|
666
|
-
const response = await fetch(config.config.baseURL + config.endPoint, config.config);
|
|
667
|
-
return response; // Native Response object
|
|
668
|
-
})
|
|
669
|
-
|
|
670
|
-
// Step 5: Response Interceptor 1
|
|
671
|
-
.then(response => {
|
|
672
|
-
if (!response.ok) throw new Error('Bad response');
|
|
673
|
-
return response; // Response object passed through
|
|
674
|
-
})
|
|
675
|
-
|
|
676
|
-
// Step 6: Response Interceptor 2
|
|
677
|
-
.then(response => {
|
|
678
|
-
console.log('Response received:', response.status);
|
|
679
|
-
return response; // Final Response object
|
|
680
|
-
})
|
|
681
|
-
|
|
682
|
-
// Result: User gets the Response object
|
|
683
|
-
```
|
|
684
|
-
|
|
685
|
-
**Error Handling Example:**
|
|
686
|
-
|
|
687
|
-
```javascript
|
|
688
|
-
// If Request Interceptor throws:
|
|
689
|
-
.then(config => {
|
|
690
|
-
throw new Error('Invalid token'); // Sync error
|
|
691
|
-
})
|
|
692
|
-
|
|
693
|
-
// Caught by try-catch, passed to errorHandler:
|
|
694
|
-
catch (error) {
|
|
695
|
-
if (errorHandler) {
|
|
696
|
-
return errorHandler(error); // Can recover
|
|
697
|
-
} else {
|
|
698
|
-
return Promise.reject(error); // Propagate
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// If fetch fails (network error, timeout):
|
|
703
|
-
.then(async config => {
|
|
704
|
-
const response = await fetch(...); // Throws on timeout
|
|
705
|
-
return response;
|
|
706
|
-
})
|
|
707
|
-
|
|
708
|
-
// Second parameter of .then() catches it:
|
|
709
|
-
.then(successHandler, (error) => {
|
|
710
|
-
if (errorHandler) {
|
|
711
|
-
return errorHandler(error);
|
|
712
|
-
} else {
|
|
124
|
+
},
|
|
125
|
+
(error) => {
|
|
126
|
+
console.error('Request failed:', error);
|
|
713
127
|
return Promise.reject(error);
|
|
714
128
|
}
|
|
715
|
-
|
|
716
|
-
```
|
|
717
|
-
|
|
718
|
-
**Why Use This Pattern?**
|
|
719
|
-
|
|
720
|
-
1. **Sequential Execution**: Each interceptor waits for previous one
|
|
721
|
-
2. **Error Recovery**: Interceptors can catch and fix errors
|
|
722
|
-
3. **Immutable Chain**: Original promise isn't modified
|
|
723
|
-
4. **Type Safety**: Each step expects specific input/output format
|
|
724
|
-
5. **Debugging**: Easy to add console.logs at each step
|
|
725
|
-
|
|
726
|
-
**Key Points:**
|
|
727
|
-
|
|
728
|
-
- Interceptors must return a value (config or response)
|
|
729
|
-
- Errors can be caught and handled at any point
|
|
730
|
-
- The chain is built once per request
|
|
731
|
-
- All interceptors share the same promise chain
|
|
732
|
-
- `bind(this)` ensures `#dispatchRequest` maintains class context
|
|
733
|
-
|
|
734
|
-
---
|
|
735
|
-
|
|
736
|
-
## Architecture & Design Decisions
|
|
737
|
-
|
|
738
|
-
### Why Classes?
|
|
739
|
-
|
|
740
|
-
- Encapsulation of state (config, interceptors)
|
|
741
|
-
- Private methods (#) for internal logic
|
|
742
|
-
- Instance creation for multiple API clients
|
|
743
|
-
- Clear separation of concerns
|
|
744
|
-
|
|
745
|
-
### Why Private Methods (#)?
|
|
746
|
-
|
|
747
|
-
```javascript
|
|
748
|
-
#request()
|
|
749
|
-
#dispatchRequest()
|
|
750
|
-
#mergeConfig()
|
|
751
|
-
#mergeConfigs()
|
|
752
|
-
```
|
|
753
|
-
|
|
754
|
-
- Hide implementation details
|
|
755
|
-
- Prevent external modification
|
|
756
|
-
- Clear API surface (only public methods exposed)
|
|
757
|
-
- Follows encapsulation principles
|
|
758
|
-
|
|
759
|
-
### Why Async/Await in #dispatchRequest?
|
|
760
|
-
|
|
761
|
-
```javascript
|
|
762
|
-
// Way 1: Promise chain
|
|
763
|
-
return fetch(url, config).finally(() => clearTimeout(timeOutId));
|
|
764
|
-
|
|
765
|
-
// Way 2: Async/Await (chosen)
|
|
766
|
-
try {
|
|
767
|
-
const response = await fetch(url, config);
|
|
768
|
-
return response;
|
|
769
|
-
} finally {
|
|
770
|
-
clearTimeout(timeOutId);
|
|
771
|
-
}
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
**Chosen Way 2 because:**
|
|
775
|
-
- More readable for error handling
|
|
776
|
-
- Finally block guaranteed to run
|
|
777
|
-
- Easier to debug with stack traces
|
|
778
|
-
- Synchronous-looking async code
|
|
779
|
-
|
|
780
|
-
### Why Bind Context?
|
|
781
|
-
|
|
782
|
-
```javascript
|
|
783
|
-
successHandler: this.#dispatchRequest.bind(this)
|
|
784
|
-
```
|
|
785
|
-
|
|
786
|
-
Without `bind(this)`:
|
|
787
|
-
```javascript
|
|
788
|
-
// Inside interceptor chain loop
|
|
789
|
-
successHandler(config); // 'this' is undefined in #dispatchRequest!
|
|
790
|
-
```
|
|
791
|
-
|
|
792
|
-
With `bind(this)`:
|
|
793
|
-
```javascript
|
|
794
|
-
// 'this' correctly refers to Fetchify instance
|
|
795
|
-
this.config, this.requestInterceptors, etc. are accessible
|
|
129
|
+
);
|
|
796
130
|
```
|
|
797
131
|
|
|
798
|
-
### Why Two Merge Methods?
|
|
799
|
-
|
|
800
|
-
1. **#mergeConfig(newConfig)**: Merges with `this.config`
|
|
801
|
-
- Used in constructor and #request
|
|
802
|
-
- Instance-specific merging
|
|
803
|
-
|
|
804
|
-
2. **#mergeConfigs(config1, config2)**: Merges two arbitrary configs
|
|
805
|
-
- Used in #dispatchRequest
|
|
806
|
-
- Doesn't depend on instance state
|
|
807
|
-
|
|
808
|
-
Separation provides flexibility and reusability.
|
|
809
|
-
|
|
810
132
|
---
|
|
811
133
|
|
|
812
|
-
##
|
|
134
|
+
## Advanced Examples
|
|
813
135
|
|
|
814
|
-
### Complete
|
|
136
|
+
### Complete Setup with Authentication
|
|
815
137
|
|
|
816
138
|
```javascript
|
|
817
139
|
import fetchify from '@rutansh0101/fetchify';
|
|
818
140
|
|
|
819
|
-
// 1. Create instance with base config
|
|
820
141
|
const api = fetchify.create({
|
|
821
142
|
baseURL: 'https://api.example.com',
|
|
822
143
|
timeout: 5000,
|
|
823
144
|
headers: {
|
|
824
|
-
'Content-Type': 'application/json'
|
|
825
|
-
'X-API-Version': 'v1'
|
|
145
|
+
'Content-Type': 'application/json'
|
|
826
146
|
}
|
|
827
147
|
});
|
|
828
148
|
|
|
829
|
-
//
|
|
830
|
-
api.addRequestInterceptor(
|
|
831
|
-
(
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
config.config.headers['Authorization'] = `Bearer ${token}`;
|
|
835
|
-
}
|
|
836
|
-
console.log('→ Request:', config.config.method, config.endPoint);
|
|
837
|
-
return config;
|
|
838
|
-
},
|
|
839
|
-
(error) => {
|
|
840
|
-
console.error('Request interceptor failed:', error);
|
|
841
|
-
return Promise.reject(error);
|
|
149
|
+
// Add auth token to all requests
|
|
150
|
+
api.addRequestInterceptor((config) => {
|
|
151
|
+
const token = localStorage.getItem('token');
|
|
152
|
+
if (token) {
|
|
153
|
+
config.config.headers['Authorization'] = `Bearer ${token}`;
|
|
842
154
|
}
|
|
843
|
-
|
|
155
|
+
return config;
|
|
156
|
+
});
|
|
844
157
|
|
|
845
|
-
//
|
|
158
|
+
// Handle errors globally
|
|
846
159
|
api.addResponseInterceptor(
|
|
847
160
|
(response) => {
|
|
848
|
-
console.log('← Response:', response.status, response.statusText);
|
|
849
|
-
|
|
850
161
|
if (!response.ok) {
|
|
851
|
-
if (response.status === 401) {
|
|
852
|
-
localStorage.removeItem('authToken');
|
|
853
|
-
window.location.href = '/login';
|
|
854
|
-
}
|
|
855
162
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
856
163
|
}
|
|
857
|
-
|
|
858
164
|
return response;
|
|
859
165
|
},
|
|
860
166
|
(error) => {
|
|
861
167
|
if (error.message.includes('timeout')) {
|
|
862
|
-
alert('Request timed out. Please
|
|
168
|
+
alert('Request timed out. Please try again.');
|
|
863
169
|
}
|
|
864
170
|
return Promise.reject(error);
|
|
865
171
|
}
|
|
866
172
|
);
|
|
867
173
|
|
|
868
|
-
//
|
|
869
|
-
async function
|
|
174
|
+
// Use the API
|
|
175
|
+
async function fetchUsers() {
|
|
870
176
|
try {
|
|
871
|
-
|
|
872
|
-
const
|
|
873
|
-
|
|
874
|
-
console.log('Users:', users);
|
|
875
|
-
|
|
876
|
-
// POST request with custom timeout
|
|
877
|
-
const newUser = await api.post('/users', {
|
|
878
|
-
body: JSON.stringify({
|
|
879
|
-
name: 'John Doe',
|
|
880
|
-
email: 'john@example.com'
|
|
881
|
-
}),
|
|
882
|
-
timeout: 10000 // Override default timeout
|
|
883
|
-
});
|
|
884
|
-
const created = await newUser.json();
|
|
885
|
-
console.log('Created user:', created);
|
|
886
|
-
|
|
887
|
-
// PUT request
|
|
888
|
-
const updateResponse = await api.put('/users/1', {
|
|
889
|
-
body: JSON.stringify({ name: 'Jane Doe' })
|
|
890
|
-
});
|
|
891
|
-
|
|
892
|
-
// PATCH request
|
|
893
|
-
const patchResponse = await api.patch('/users/1', {
|
|
894
|
-
body: JSON.stringify({ email: 'jane@example.com' })
|
|
895
|
-
});
|
|
896
|
-
|
|
897
|
-
// DELETE request
|
|
898
|
-
await api.delete('/users/1');
|
|
899
|
-
|
|
177
|
+
const response = await api.get('/users');
|
|
178
|
+
const users = await response.json();
|
|
179
|
+
console.log(users);
|
|
900
180
|
} catch (error) {
|
|
901
|
-
console.error('
|
|
181
|
+
console.error('Failed to fetch users:', error);
|
|
902
182
|
}
|
|
903
183
|
}
|
|
904
|
-
|
|
905
|
-
example();
|
|
906
184
|
```
|
|
907
185
|
|
|
908
186
|
### Multiple API Instances
|
|
909
187
|
|
|
910
188
|
```javascript
|
|
911
|
-
// Different APIs with different configs
|
|
912
189
|
const mainAPI = fetchify.create({
|
|
913
190
|
baseURL: 'https://api.example.com',
|
|
914
191
|
timeout: 5000
|
|
@@ -919,349 +196,93 @@ const authAPI = fetchify.create({
|
|
|
919
196
|
timeout: 10000
|
|
920
197
|
});
|
|
921
198
|
|
|
922
|
-
|
|
923
|
-
baseURL: 'https://analytics.example.com',
|
|
924
|
-
timeout: 3000,
|
|
925
|
-
headers: {
|
|
926
|
-
'X-Analytics-Key': 'secret123'
|
|
927
|
-
}
|
|
928
|
-
});
|
|
929
|
-
|
|
930
|
-
// Use them independently
|
|
199
|
+
// Use independently
|
|
931
200
|
await mainAPI.get('/users');
|
|
932
201
|
await authAPI.post('/login', { body: credentials });
|
|
933
|
-
await analyticsAPI.post('/track', { body: event });
|
|
934
202
|
```
|
|
935
203
|
|
|
936
204
|
---
|
|
937
205
|
|
|
938
206
|
## Error Handling
|
|
939
207
|
|
|
940
|
-
### Types of Errors
|
|
941
|
-
|
|
942
|
-
1. **Network Errors**: No internet, DNS failure, etc.
|
|
943
|
-
2. **Timeout Errors**: Request exceeded timeout duration
|
|
944
|
-
3. **HTTP Errors**: 4xx, 5xx status codes
|
|
945
|
-
4. **Interceptor Errors**: Thrown by custom interceptor logic
|
|
946
|
-
|
|
947
|
-
### Error Handling Pattern
|
|
948
|
-
|
|
949
208
|
```javascript
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
if (!response.ok) {
|
|
956
|
-
throw new Error(`HTTP ${response.status}`);
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
const data = await response.json();
|
|
960
|
-
return data;
|
|
961
|
-
|
|
962
|
-
} catch (error) {
|
|
963
|
-
// Timeout error
|
|
964
|
-
if (error.message.includes('timeout')) {
|
|
965
|
-
console.error('Request timed out');
|
|
966
|
-
return null;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// Network error
|
|
970
|
-
if (error.message.includes('fetch')) {
|
|
971
|
-
console.error('Network error');
|
|
972
|
-
return null;
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// Other errors
|
|
976
|
-
console.error('Unknown error:', error);
|
|
977
|
-
throw error;
|
|
209
|
+
try {
|
|
210
|
+
const response = await api.get('/users', { timeout: 3000 });
|
|
211
|
+
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
throw new Error(`HTTP ${response.status}`);
|
|
978
214
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
switch(response.status) {
|
|
989
|
-
case 401:
|
|
990
|
-
console.error('Unauthorized - redirecting to login');
|
|
991
|
-
window.location.href = '/login';
|
|
992
|
-
break;
|
|
993
|
-
case 403:
|
|
994
|
-
console.error('Forbidden - insufficient permissions');
|
|
995
|
-
break;
|
|
996
|
-
case 404:
|
|
997
|
-
console.error('Resource not found');
|
|
998
|
-
break;
|
|
999
|
-
case 500:
|
|
1000
|
-
console.error('Server error');
|
|
1001
|
-
break;
|
|
1002
|
-
}
|
|
1003
|
-
return response;
|
|
1004
|
-
},
|
|
1005
|
-
(error) => {
|
|
1006
|
-
// Log all errors globally
|
|
1007
|
-
console.error('[Global Error Handler]', error);
|
|
1008
|
-
|
|
1009
|
-
// Send error to monitoring service
|
|
1010
|
-
// sendToSentry(error);
|
|
1011
|
-
|
|
1012
|
-
return Promise.reject(error);
|
|
215
|
+
|
|
216
|
+
const data = await response.json();
|
|
217
|
+
return data;
|
|
218
|
+
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (error.message.includes('timeout')) {
|
|
221
|
+
console.error('Request timed out');
|
|
222
|
+
} else {
|
|
223
|
+
console.error('Request failed:', error);
|
|
1013
224
|
}
|
|
1014
|
-
|
|
225
|
+
}
|
|
1015
226
|
```
|
|
1016
227
|
|
|
1017
228
|
---
|
|
1018
229
|
|
|
1019
230
|
## API Reference
|
|
1020
231
|
|
|
1021
|
-
### fetchify.create(config)
|
|
232
|
+
### `fetchify.create(config)`
|
|
1022
233
|
|
|
1023
234
|
Creates a new Fetchify instance.
|
|
1024
235
|
|
|
1025
|
-
**
|
|
1026
|
-
- `
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
- `headers` (Object): Default headers for all requests
|
|
1030
|
-
|
|
1031
|
-
**Returns:** Fetchify instance
|
|
1032
|
-
|
|
1033
|
-
**Example:**
|
|
1034
|
-
```javascript
|
|
1035
|
-
const api = fetchify.create({
|
|
1036
|
-
baseURL: 'https://api.example.com',
|
|
1037
|
-
timeout: 5000,
|
|
1038
|
-
headers: { 'Authorization': 'Bearer token' }
|
|
1039
|
-
});
|
|
1040
|
-
```
|
|
1041
|
-
|
|
1042
|
-
---
|
|
1043
|
-
|
|
1044
|
-
### instance.get(endpoint, config)
|
|
1045
|
-
|
|
1046
|
-
Performs a GET request.
|
|
1047
|
-
|
|
1048
|
-
**Parameters:**
|
|
1049
|
-
- `endpoint` (String): API endpoint (appended to baseURL)
|
|
1050
|
-
- `config` (Object, optional): Request-specific config
|
|
1051
|
-
- `timeout` (Number): Override default timeout
|
|
1052
|
-
- `headers` (Object): Additional headers
|
|
1053
|
-
- Any other fetch options
|
|
1054
|
-
|
|
1055
|
-
**Returns:** Promise<Response>
|
|
1056
|
-
|
|
1057
|
-
**Example:**
|
|
1058
|
-
```javascript
|
|
1059
|
-
const response = await api.get('/users', { timeout: 3000 });
|
|
1060
|
-
const users = await response.json();
|
|
1061
|
-
```
|
|
1062
|
-
|
|
1063
|
-
---
|
|
1064
|
-
|
|
1065
|
-
### instance.post(endpoint, config)
|
|
1066
|
-
|
|
1067
|
-
Performs a POST request.
|
|
1068
|
-
|
|
1069
|
-
**Parameters:**
|
|
1070
|
-
- `endpoint` (String): API endpoint
|
|
1071
|
-
- `config` (Object, optional): Request-specific config
|
|
1072
|
-
- `body` (String): Request body (usually JSON.stringify)
|
|
1073
|
-
- `timeout` (Number): Override default timeout
|
|
1074
|
-
- `headers` (Object): Additional headers
|
|
1075
|
-
|
|
1076
|
-
**Returns:** Promise<Response>
|
|
1077
|
-
|
|
1078
|
-
**Example:**
|
|
1079
|
-
```javascript
|
|
1080
|
-
const response = await api.post('/users', {
|
|
1081
|
-
body: JSON.stringify({ name: 'John' }),
|
|
1082
|
-
timeout: 10000
|
|
1083
|
-
});
|
|
1084
|
-
```
|
|
1085
|
-
|
|
1086
|
-
---
|
|
1087
|
-
|
|
1088
|
-
### instance.put(endpoint, config)
|
|
236
|
+
**Config Options:**
|
|
237
|
+
- `baseURL` (String) - Base URL for all requests
|
|
238
|
+
- `timeout` (Number) - Default timeout in milliseconds (default: 1000)
|
|
239
|
+
- `headers` (Object) - Default headers
|
|
1089
240
|
|
|
1090
|
-
|
|
241
|
+
### HTTP Methods
|
|
1091
242
|
|
|
1092
|
-
|
|
243
|
+
All methods return a Promise that resolves to the fetch Response object.
|
|
1093
244
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
```
|
|
1100
|
-
|
|
1101
|
-
---
|
|
1102
|
-
|
|
1103
|
-
### instance.patch(endpoint, config)
|
|
245
|
+
- `instance.get(endpoint, config)` - GET request
|
|
246
|
+
- `instance.post(endpoint, config)` - POST request
|
|
247
|
+
- `instance.put(endpoint, config)` - PUT request
|
|
248
|
+
- `instance.patch(endpoint, config)` - PATCH request
|
|
249
|
+
- `instance.delete(endpoint, config)` - DELETE request
|
|
1104
250
|
|
|
1105
|
-
|
|
251
|
+
### Interceptors
|
|
1106
252
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
**Example:**
|
|
1110
|
-
```javascript
|
|
1111
|
-
await api.patch('/users/1', {
|
|
1112
|
-
body: JSON.stringify({ email: 'newemail@example.com' })
|
|
1113
|
-
});
|
|
1114
|
-
```
|
|
253
|
+
- `instance.addRequestInterceptor(successHandler, errorHandler)` - Intercept requests
|
|
254
|
+
- `instance.addResponseInterceptor(successHandler, errorHandler)` - Intercept responses
|
|
1115
255
|
|
|
1116
256
|
---
|
|
1117
257
|
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
Performs a DELETE request.
|
|
1121
|
-
|
|
1122
|
-
**Parameters:**
|
|
1123
|
-
- `endpoint` (String): API endpoint
|
|
1124
|
-
- `config` (Object, optional): Request-specific config
|
|
258
|
+
## Quick Reference
|
|
1125
259
|
|
|
1126
|
-
**Returns:** Promise<Response>
|
|
1127
|
-
|
|
1128
|
-
**Example:**
|
|
1129
|
-
```javascript
|
|
1130
|
-
await api.delete('/users/1');
|
|
1131
|
-
```
|
|
1132
|
-
|
|
1133
|
-
---
|
|
1134
|
-
|
|
1135
|
-
### instance.addRequestInterceptor(successHandler, errorHandler)
|
|
1136
|
-
|
|
1137
|
-
Adds a request interceptor.
|
|
1138
|
-
|
|
1139
|
-
**Parameters:**
|
|
1140
|
-
- `successHandler` (Function): Called before request
|
|
1141
|
-
- Receives: `{ endPoint, config }`
|
|
1142
|
-
- Must return: Modified `{ endPoint, config }`
|
|
1143
|
-
- `errorHandler` (Function, optional): Called if previous interceptor failed
|
|
1144
|
-
- Receives: Error object
|
|
1145
|
-
- Must return: Rejected promise or recovery value
|
|
1146
|
-
|
|
1147
|
-
**Example:**
|
|
1148
260
|
```javascript
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
config.config.headers['X-Timestamp'] = Date.now();
|
|
1152
|
-
return config;
|
|
1153
|
-
},
|
|
1154
|
-
(error) => {
|
|
1155
|
-
console.error('Request error:', error);
|
|
1156
|
-
return Promise.reject(error);
|
|
1157
|
-
}
|
|
1158
|
-
);
|
|
1159
|
-
```
|
|
1160
|
-
|
|
1161
|
-
---
|
|
1162
|
-
|
|
1163
|
-
### instance.addResponseInterceptor(successHandler, errorHandler)
|
|
1164
|
-
|
|
1165
|
-
Adds a response interceptor.
|
|
261
|
+
// Create
|
|
262
|
+
const api = fetchify.create({ baseURL: '...', timeout: 5000 });
|
|
1166
263
|
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
- Must return: Rejected promise or recovery value
|
|
264
|
+
// Requests
|
|
265
|
+
await api.get('/path');
|
|
266
|
+
await api.post('/path', { body: JSON.stringify(data) });
|
|
267
|
+
await api.put('/path', { body: JSON.stringify(data) });
|
|
268
|
+
await api.patch('/path', { body: JSON.stringify(data) });
|
|
269
|
+
await api.delete('/path');
|
|
1174
270
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
api.addResponseInterceptor(
|
|
1178
|
-
(response) => {
|
|
1179
|
-
console.log('Status:', response.status);
|
|
1180
|
-
return response;
|
|
1181
|
-
},
|
|
1182
|
-
(error) => {
|
|
1183
|
-
if (error.message.includes('timeout')) {
|
|
1184
|
-
alert('Request timed out');
|
|
1185
|
-
}
|
|
1186
|
-
return Promise.reject(error);
|
|
1187
|
-
}
|
|
1188
|
-
);
|
|
271
|
+
// Interceptors
|
|
272
|
+
api.addRequestInterceptor((config) => { return config; });
|
|
273
|
+
api.addResponseInterceptor((response) => { return response; });
|
|
1189
274
|
```
|
|
1190
275
|
|
|
1191
276
|
---
|
|
1192
277
|
|
|
1193
|
-
## Comparison with Axios
|
|
1194
|
-
|
|
1195
|
-
| Feature | Fetchify | Axios |
|
|
1196
|
-
|---------|----------|-------|
|
|
1197
|
-
| Size | ~5KB | ~20KB |
|
|
1198
|
-
| Dependencies | None (native fetch) | Standalone library |
|
|
1199
|
-
| Browser Support | Modern browsers | All browsers (polyfills) |
|
|
1200
|
-
| Interceptors | ✅ | ✅ |
|
|
1201
|
-
| Timeout | ✅ | ✅ |
|
|
1202
|
-
| Request Cancellation | ✅ (AbortController) | ✅ (CancelToken) |
|
|
1203
|
-
| Automatic JSON Transform | ❌ (manual) | ✅ |
|
|
1204
|
-
| Progress Events | ❌ | ✅ |
|
|
1205
|
-
| TypeScript | ❌ | ✅ |
|
|
1206
|
-
|
|
1207
|
-
---
|
|
1208
|
-
|
|
1209
|
-
## Future Enhancements
|
|
1210
|
-
|
|
1211
|
-
Potential features to add:
|
|
1212
|
-
- Retry logic with exponential backoff
|
|
1213
|
-
- Request caching
|
|
1214
|
-
- Automatic JSON parsing
|
|
1215
|
-
- Progress events for uploads
|
|
1216
|
-
- TypeScript definitions
|
|
1217
|
-
- Request/response transformation helpers
|
|
1218
|
-
- CSRF token handling
|
|
1219
|
-
- File upload helpers
|
|
1220
|
-
|
|
1221
|
-
---
|
|
1222
|
-
|
|
1223
278
|
## License
|
|
1224
279
|
|
|
1225
280
|
MIT License - Free to use and modify
|
|
1226
281
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
## Contributing
|
|
282
|
+
## Author
|
|
1230
283
|
|
|
1231
|
-
|
|
284
|
+
Rutansh Chawla
|
|
1232
285
|
|
|
1233
286
|
---
|
|
1234
287
|
|
|
1235
|
-
**Last Updated:** January 2026
|
|
1236
288
|
**Version:** 1.0.0
|
|
1237
|
-
**Author:** Rutansh Chawla
|
|
1238
|
-
|
|
1239
|
-
---
|
|
1240
|
-
|
|
1241
|
-
## Quick Reference Card
|
|
1242
|
-
|
|
1243
|
-
```javascript
|
|
1244
|
-
// Create instance
|
|
1245
|
-
const api = fetchify.create({ baseURL: '...', timeout: 5000 });
|
|
1246
|
-
|
|
1247
|
-
// Requests
|
|
1248
|
-
await api.get('/path', { timeout: 3000 });
|
|
1249
|
-
await api.post('/path', { body: JSON.stringify(data) });
|
|
1250
|
-
await api.put('/path', { body: JSON.stringify(data) });
|
|
1251
|
-
await api.patch('/path', { body: JSON.stringify(data) });
|
|
1252
|
-
await api.delete('/path');
|
|
1253
|
-
|
|
1254
|
-
// Interceptors
|
|
1255
|
-
api.addRequestInterceptor((config) => { /* modify */ return config; });
|
|
1256
|
-
api.addResponseInterceptor((response) => { /* handle */ return response; });
|
|
1257
|
-
|
|
1258
|
-
// Error handling
|
|
1259
|
-
try {
|
|
1260
|
-
const res = await api.get('/path');
|
|
1261
|
-
const data = await res.json();
|
|
1262
|
-
} catch (error) {
|
|
1263
|
-
console.error(error.message);
|
|
1264
|
-
}
|
|
1265
|
-
```
|
|
1266
|
-
|
|
1267
|
-
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rutansh0101/fetchify",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A modern, lightweight HTTP client library built on top of the native Fetch API with axios-like interface, interceptors, and timeout support",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Rutansh Chawla",
|