create-request 0.0.1-beta.14 → 0.0.1-beta.15
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 +139 -122
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/create-request)
|
|
4
4
|
[](https://github.com/DanielAmenou/create-request/blob/main/LICENSE)
|
|
5
5
|
[](https://bundlephobia.com/package/create-request)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
6
7
|
|
|
7
8
|
`create-request` is a modern TypeScript library that transforms how you make API calls. Built as an elegant wrapper around the native Fetch API, it provides a chainable, fluent interface that dramatically reduces boilerplate while adding powerful features like automatic retries, timeout handling, and comprehensive error management.
|
|
8
9
|
|
|
@@ -13,7 +14,9 @@
|
|
|
13
14
|
- [Installation](#installation)
|
|
14
15
|
- [Basic Usage](#basic-usage)
|
|
15
16
|
- [Advanced Usage](#advanced-usage)
|
|
17
|
+
- [TypeScript Support](#typescript-support)
|
|
16
18
|
- [CSRF Protection](#csrf-protection)
|
|
19
|
+
- [Performance Considerations](#performance-considerations)
|
|
17
20
|
- [Browser Support](#browser-support)
|
|
18
21
|
- [License](#license)
|
|
19
22
|
|
|
@@ -36,8 +39,9 @@
|
|
|
36
39
|
|
|
37
40
|
**API interactions often require repetitive code patterns** - handling HTTP status checks, parsing responses, managing errors, and dealing with TypeScript types. `create-request` provides a clean, efficient solution with an elegant API that separates request building from execution:
|
|
38
41
|
|
|
42
|
+
### With Regular Fetch
|
|
43
|
+
|
|
39
44
|
```typescript
|
|
40
|
-
// Before: Regular fetch
|
|
41
45
|
async function createUser(userData) {
|
|
42
46
|
try {
|
|
43
47
|
const response = await fetch("https://api.example.com/users", {
|
|
@@ -59,17 +63,20 @@ async function createUser(userData) {
|
|
|
59
63
|
throw error;
|
|
60
64
|
}
|
|
61
65
|
}
|
|
66
|
+
```
|
|
62
67
|
|
|
63
|
-
|
|
68
|
+
### With create-request
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
64
71
|
import create from "create-request";
|
|
65
72
|
|
|
66
73
|
function createUser(userData) {
|
|
67
74
|
return create
|
|
68
75
|
.post()
|
|
69
76
|
.withBasicAuth("username", "password")
|
|
70
|
-
.withBody(userData)
|
|
77
|
+
.withBody(userData) // Content-Type automatically set to application/json
|
|
71
78
|
.sendTo("https://api.example.com/users")
|
|
72
|
-
.getData<User>()
|
|
79
|
+
.getData<User>() // Type-safe response handling
|
|
73
80
|
.catch(error => {
|
|
74
81
|
console.error("Fetch error:", error);
|
|
75
82
|
throw error;
|
|
@@ -80,13 +87,14 @@ function createUser(userData) {
|
|
|
80
87
|
## Installation
|
|
81
88
|
|
|
82
89
|
```bash
|
|
90
|
+
# npm
|
|
83
91
|
npm install create-request
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Or with yarn:
|
|
87
92
|
|
|
88
|
-
|
|
93
|
+
# yarn
|
|
89
94
|
yarn add create-request
|
|
95
|
+
|
|
96
|
+
# pnpm
|
|
97
|
+
pnpm add create-request
|
|
90
98
|
```
|
|
91
99
|
|
|
92
100
|
## Basic Usage
|
|
@@ -97,13 +105,13 @@ yarn add create-request
|
|
|
97
105
|
import create from "create-request";
|
|
98
106
|
|
|
99
107
|
// Create different request types
|
|
100
|
-
const getRequest = create.get();
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const deleteRequest = create.del();
|
|
105
|
-
const
|
|
106
|
-
const optionsRequest = create.options();
|
|
108
|
+
const getRequest = create.get(); // GET
|
|
109
|
+
const putRequest = create.put(); // PUT
|
|
110
|
+
const postRequest = create.post(); // POST
|
|
111
|
+
const patchRequest = create.patch(); // PATCH
|
|
112
|
+
const deleteRequest = create.del(); // DELETE
|
|
113
|
+
const headRequest = create.head(); // HEAD
|
|
114
|
+
const optionsRequest = create.options(); // OPTIONS
|
|
107
115
|
```
|
|
108
116
|
|
|
109
117
|
### Request Configuration
|
|
@@ -111,7 +119,7 @@ const optionsRequest = create.options();
|
|
|
111
119
|
The library provides a comprehensive set of configuration methods that can be chained together to customize your requests:
|
|
112
120
|
|
|
113
121
|
```typescript
|
|
114
|
-
import create, { RequestPriority, CredentialsPolicy, RequestMode,
|
|
122
|
+
import create, { RequestPriority, CredentialsPolicy, RequestMode, RedirectMode, ReferrerPolicy } from "create-request";
|
|
115
123
|
|
|
116
124
|
// Configure request options
|
|
117
125
|
const request = create
|
|
@@ -127,6 +135,8 @@ const request = create
|
|
|
127
135
|
.withRetries(3) // Retry up to 3 times on failure
|
|
128
136
|
.onRetry(({ attempt, error }) => {
|
|
129
137
|
console.log(`Attempt ${attempt} failed: ${error.message}. Retrying...`);
|
|
138
|
+
// You can implement backoff strategy here
|
|
139
|
+
return new Promise(resolve => setTimeout(resolve, attempt * 1000));
|
|
130
140
|
})
|
|
131
141
|
|
|
132
142
|
// Authentication methods
|
|
@@ -181,10 +191,10 @@ const products = await apiBase.sendTo("https://api.example.com/products").getDat
|
|
|
181
191
|
### Request Bodies (POST/PUT/PATCH)
|
|
182
192
|
|
|
183
193
|
```typescript
|
|
184
|
-
// JSON body
|
|
194
|
+
// JSON body (Content-Type automatically set to application/json)
|
|
185
195
|
const jsonRequest = create.post().withBody({ name: "John", age: 30 });
|
|
186
196
|
|
|
187
|
-
// String body
|
|
197
|
+
// String body (Content-Type automatically set to text/plain)
|
|
188
198
|
const textRequest = create.post().withBody("Plain text content");
|
|
189
199
|
|
|
190
200
|
// Form data
|
|
@@ -193,6 +203,13 @@ formData.append("name", "John");
|
|
|
193
203
|
formData.append("file", fileBlob);
|
|
194
204
|
|
|
195
205
|
const formRequest = create.post().withBody(formData);
|
|
206
|
+
|
|
207
|
+
// URLSearchParams (typically used for application/x-www-form-urlencoded)
|
|
208
|
+
const params = new URLSearchParams();
|
|
209
|
+
params.append("username", "john");
|
|
210
|
+
params.append("password", "secret");
|
|
211
|
+
|
|
212
|
+
const formUrlEncodedRequest = create.post().withBody(params);
|
|
196
213
|
```
|
|
197
214
|
|
|
198
215
|
### Executing Requests
|
|
@@ -215,39 +232,6 @@ const userData = await request.sendTo("https://api.example.com/users").getData(d
|
|
|
215
232
|
const fullData = await request.sendTo("https://api.example.com/data").getData();
|
|
216
233
|
```
|
|
217
234
|
|
|
218
|
-
### Data Selection
|
|
219
|
-
|
|
220
|
-
The `getData` method provides a powerful way to extract and transform specific data from API responses:
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
// Extract specific properties from nested structures
|
|
224
|
-
const posts = await request.sendTo("https://api.example.com/feed").getData(data => data.feed.posts);
|
|
225
|
-
|
|
226
|
-
// Transform data in the selector function
|
|
227
|
-
const usernames = await request.sendTo("https://api.example.com/users").getData(data => data.users.map(user => user.username));
|
|
228
|
-
|
|
229
|
-
// Apply filtering in the selector
|
|
230
|
-
const activeUsers = await request.sendTo("https://api.example.com/users").getData(data => data.users.filter(user => user.isActive));
|
|
231
|
-
|
|
232
|
-
// Combine data from complex nested structures
|
|
233
|
-
const combinedData = await request.sendTo("https://api.example.com/dashboard").getData(data => ({
|
|
234
|
-
userCount: data.stats.users.total,
|
|
235
|
-
recentPosts: data.content.recent.slice(0, 5),
|
|
236
|
-
notifications: data.user.notifications.unread,
|
|
237
|
-
}));
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
When a selector fails, the error message will contain the original response data to help diagnose the issue:
|
|
241
|
-
|
|
242
|
-
```typescript
|
|
243
|
-
try {
|
|
244
|
-
const result = await request.sendTo("https://api.example.com/data").getData(data => data.results.items); // Will fail if structure is different
|
|
245
|
-
} catch (error) {
|
|
246
|
-
console.error(error);
|
|
247
|
-
// Error message includes the original response data
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
235
|
### Error Handling
|
|
252
236
|
|
|
253
237
|
All errors from requests are instances of `RequestError` with detailed information:
|
|
@@ -318,6 +302,26 @@ const authRequest = create.get().withBearerToken("token123").withTimeout(5000).w
|
|
|
318
302
|
// Reuse for different endpoints
|
|
319
303
|
const users = await authRequest.sendTo("https://api.example.com/users").getJson();
|
|
320
304
|
const products = await authRequest.sendTo("https://api.example.com/products").getJson();
|
|
305
|
+
|
|
306
|
+
// You can also create application-wide base requests
|
|
307
|
+
const apiBase = () =>
|
|
308
|
+
create
|
|
309
|
+
.get()
|
|
310
|
+
.withHeaders({
|
|
311
|
+
"X-API-Version": "1.2",
|
|
312
|
+
"Accept-Language": "en-US",
|
|
313
|
+
})
|
|
314
|
+
.withBearerToken(getAuthToken()) // Get fresh token each time
|
|
315
|
+
.withTimeout(5000);
|
|
316
|
+
|
|
317
|
+
// Use throughout your application
|
|
318
|
+
function getUsers() {
|
|
319
|
+
return apiBase().sendTo("https://api.example.com/users").getData();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function getProducts() {
|
|
323
|
+
return apiBase().sendTo("https://api.example.com/products").getData();
|
|
324
|
+
}
|
|
321
325
|
```
|
|
322
326
|
|
|
323
327
|
### Request Cancellation
|
|
@@ -333,81 +337,48 @@ setTimeout(() => controller.abort(), 2000);
|
|
|
333
337
|
try {
|
|
334
338
|
const data = await request.sendTo("https://api.example.com/slow-endpoint").getJson();
|
|
335
339
|
} catch (error) {
|
|
336
|
-
|
|
337
|
-
|
|
340
|
+
if (error.name === "AbortError") {
|
|
341
|
+
console.log("Request was cancelled by user");
|
|
342
|
+
} else if (error.timeoutError) {
|
|
343
|
+
console.log("Request timed out");
|
|
344
|
+
} else {
|
|
345
|
+
console.log("Other error:", error.message);
|
|
346
|
+
}
|
|
338
347
|
}
|
|
339
348
|
```
|
|
340
349
|
|
|
341
|
-
###
|
|
342
|
-
|
|
343
|
-
```typescript
|
|
344
|
-
interface User {
|
|
345
|
-
id: number;
|
|
346
|
-
name: string;
|
|
347
|
-
email: string;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
interface ApiResponse {
|
|
351
|
-
users: User[];
|
|
352
|
-
pagination: {
|
|
353
|
-
total: number;
|
|
354
|
-
page: number;
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
const userRequest = create.get();
|
|
359
|
-
|
|
360
|
-
// Type the full response
|
|
361
|
-
const response = await userRequest.sendTo("https://api.example.com/users").getJson<ApiResponse>();
|
|
362
|
-
|
|
363
|
-
// Or use getData with type parameters
|
|
364
|
-
const users = await userRequest.sendTo("https://api.example.com/users").getData<ApiResponse, User[]>(data => data.users);
|
|
350
|
+
### Data Selection
|
|
365
351
|
|
|
366
|
-
|
|
367
|
-
const fullData = await userRequest.sendTo("https://api.example.com/users").getData<ApiResponse>();
|
|
352
|
+
The `getData` method provides a powerful way to extract and transform specific data from API responses:
|
|
368
353
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
354
|
+
```typescript
|
|
355
|
+
// Extract specific properties from nested structures
|
|
356
|
+
const posts = await request.sendTo("https://api.example.com/feed").getData(data => data.feed.posts);
|
|
372
357
|
|
|
373
|
-
|
|
358
|
+
// Transform data in the selector function
|
|
359
|
+
const usernames = await request.sendTo("https://api.example.com/users").getData(data => data.users.map(user => user.username));
|
|
374
360
|
|
|
375
|
-
|
|
376
|
-
const
|
|
361
|
+
// Apply filtering in the selector
|
|
362
|
+
const activeUsers = await request.sendTo("https://api.example.com/users").getData(data => data.users.filter(user => user.isActive));
|
|
377
363
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
.then(data => {
|
|
385
|
-
console.log("Data:", data);
|
|
386
|
-
})
|
|
387
|
-
.catch(error => {
|
|
388
|
-
console.error("Error:", error.message);
|
|
389
|
-
});
|
|
364
|
+
// Combine data from complex nested structures
|
|
365
|
+
const combinedData = await request.sendTo("https://api.example.com/dashboard").getData(data => ({
|
|
366
|
+
userCount: data.stats.users.total,
|
|
367
|
+
recentPosts: data.content.recent.slice(0, 5),
|
|
368
|
+
notifications: data.user.notifications.unread,
|
|
369
|
+
}));
|
|
390
370
|
```
|
|
391
371
|
|
|
392
|
-
|
|
372
|
+
When a selector fails, the error message will contain the original response data to help diagnose the issue:
|
|
393
373
|
|
|
394
374
|
```typescript
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
.
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
keyGenerator: (url, method, headers) => {
|
|
403
|
-
// Include user ID from authorization header in the cache key
|
|
404
|
-
const authHeader = headers?.["Authorization"] || "";
|
|
405
|
-
const userMatch = authHeader.match(/User-(\d+)/);
|
|
406
|
-
const userId = userMatch ? userMatch[1] : "anonymous";
|
|
407
|
-
return `${method}:${userId}:${url}`;
|
|
408
|
-
},
|
|
409
|
-
})
|
|
410
|
-
.sendTo("https://api.example.com/data");
|
|
375
|
+
try {
|
|
376
|
+
// This will fail if the response structure doesn't match expectations
|
|
377
|
+
const result = await request.sendTo("https://api.example.com/data").getData(data => data.results.items);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error(error);
|
|
380
|
+
// Error message includes the original response data for debugging
|
|
381
|
+
}
|
|
411
382
|
```
|
|
412
383
|
|
|
413
384
|
### Implementing Custom Storage Providers
|
|
@@ -417,7 +388,7 @@ You can create your own storage provider by implementing the `StorageProvider` i
|
|
|
417
388
|
```typescript
|
|
418
389
|
import { StorageProvider } from "create-request";
|
|
419
390
|
|
|
420
|
-
// Create a
|
|
391
|
+
// Create a namespaced storage provider
|
|
421
392
|
export function createNamespacedStorage(namespace: string): StorageProvider {
|
|
422
393
|
return {
|
|
423
394
|
get(key: string): string | null {
|
|
@@ -466,13 +437,51 @@ const request = create
|
|
|
466
437
|
.sendTo("https://api.example.com/data");
|
|
467
438
|
```
|
|
468
439
|
|
|
469
|
-
|
|
440
|
+
## TypeScript Support
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
interface User {
|
|
444
|
+
id: number;
|
|
445
|
+
name: string;
|
|
446
|
+
email: string;
|
|
447
|
+
isActive: boolean;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
interface ApiResponse<T> {
|
|
451
|
+
data: T;
|
|
452
|
+
meta: {
|
|
453
|
+
total: number;
|
|
454
|
+
page: number;
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Type the full response
|
|
459
|
+
const response = await create.get().sendTo("https://api.example.com/users").getJson<ApiResponse<User[]>>();
|
|
460
|
+
|
|
461
|
+
// Or use getData with type parameters
|
|
462
|
+
const users = await create
|
|
463
|
+
.get()
|
|
464
|
+
.sendTo("https://api.example.com/users")
|
|
465
|
+
.getData<ApiResponse<User[]>, User[]>(data => data.data);
|
|
470
466
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
467
|
+
// TypeScript knows the types
|
|
468
|
+
users.forEach(user => {
|
|
469
|
+
console.log(`${user.name} (${user.email}): ${user.isActive ? "Active" : "Inactive"}`);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Function with proper types
|
|
473
|
+
async function getUserById(id: number): Promise<User> {
|
|
474
|
+
return create
|
|
475
|
+
.get()
|
|
476
|
+
.withQueryParam("id", id)
|
|
477
|
+
.sendTo("https://api.example.com/users")
|
|
478
|
+
.getData<ApiResponse<User[]>, User>(data => {
|
|
479
|
+
const user = data.data[0];
|
|
480
|
+
if (!user) throw new Error(`User with ID ${id} not found`);
|
|
481
|
+
return user;
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
```
|
|
476
485
|
|
|
477
486
|
## CSRF Protection
|
|
478
487
|
|
|
@@ -523,7 +532,15 @@ Most modern frameworks support CSRF protection out of the box. The library works
|
|
|
523
532
|
- **Django**: Compatible with Django's CSRF middleware
|
|
524
533
|
- **Express.js + csurf**: Works with the csurf middleware token pattern
|
|
525
534
|
|
|
526
|
-
|
|
535
|
+
## Performance Considerations
|
|
536
|
+
|
|
537
|
+
create-request is designed to be lightweight and efficient:
|
|
538
|
+
|
|
539
|
+
- **Zero Dependencies**: No extra libraries to load
|
|
540
|
+
- **Tree-Shakable**: Only import what you need
|
|
541
|
+
- **Minimal Overhead**: Thin wrapper around the native Fetch API
|
|
542
|
+
- **Memory Efficient**: Doesn't create unnecessary objects
|
|
543
|
+
- **Cache Management**: Configurable caching to reduce network requests
|
|
527
544
|
|
|
528
545
|
## Browser Support
|
|
529
546
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-request",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.15",
|
|
4
4
|
"description": "A modern, chainable wrapper for fetch with automatic retries, timeouts, comprehensive error handling, and first-class TypeScript support",
|
|
5
5
|
"main": "dist/library/index.js",
|
|
6
6
|
"module": "dist/library/index.esm.js",
|