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.
Files changed (2) hide show
  1. package/README.md +139 -122
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![npm version](https://img.shields.io/npm/v/create-request.svg)](https://www.npmjs.com/package/create-request)
4
4
  [![License](https://img.shields.io/npm/l/create-request.svg)](https://github.com/DanielAmenou/create-request/blob/main/LICENSE)
5
5
  [![Bundle Size](https://img.shields.io/bundlephobia/minzip/create-request)](https://bundlephobia.com/package/create-request)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-4.7%2B-blue)](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
- // After: With create-request
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
- ```bash
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 postRequest = create.post();
102
- const putRequest = create.put();
103
- const headRequest = create.head();
104
- const deleteRequest = create.del();
105
- const patchRequest = create.patch();
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, CacheMode, RedirectMode, ReferrerPolicy } from "create-request";
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
- // Check if request was cancelled
337
- console.log("Request was cancelled:", error.name === "AbortError");
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
- ### Working with Typed Responses
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
- // Or just get the full typed response
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
- // TypeScript knows the types
370
- console.log(users[0].name);
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
- ### Promise Chaining
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
- ```typescript
376
- const request = create.get();
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
- request
379
- .sendTo("https://api.example.com/data")
380
- .then(response => {
381
- console.log("Status:", response.status);
382
- return response.getJson();
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
- ### Advanced Caching
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
- import create, { createMemoryStorage, StorageProvider } from "create-request";
396
-
397
- // Custom key generation
398
- const request = create
399
- .get()
400
- .withCache({
401
- storage: createMemoryStorage(),
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 simple custom storage provider
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
- With custom storage providers, you can integrate with any storage system, such as:
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
- - Custom browser storage solutions
472
- - Memory caches with expiration policies
473
- - Session-based storage mechanisms
474
- - Encrypted storage providers
475
- - Third-party storage libraries
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
- When your server sends a CSRF token in a cookie or response header, `create-request` can automatically extract and include it in subsequent requests.
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.14",
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",