create-request 0.0.1-beta.13 → 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 +232 -222
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -3,9 +3,23 @@
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
 
10
+ ## Table of Contents
11
+
12
+ - [Core Features](#core-features)
13
+ - [Why create-request](#why-create-request)
14
+ - [Installation](#installation)
15
+ - [Basic Usage](#basic-usage)
16
+ - [Advanced Usage](#advanced-usage)
17
+ - [TypeScript Support](#typescript-support)
18
+ - [CSRF Protection](#csrf-protection)
19
+ - [Performance Considerations](#performance-considerations)
20
+ - [Browser Support](#browser-support)
21
+ - [License](#license)
22
+
9
23
  ## Core Features
10
24
 
11
25
  - 🚀 **Performance** - Tiny bundle size with zero dependencies
@@ -25,17 +39,18 @@
25
39
 
26
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:
27
41
 
42
+ ### With Regular Fetch
43
+
28
44
  ```typescript
29
- // Before: Regular fetch
30
45
  async function createUser(userData) {
31
46
  try {
32
- const response = await fetch('https://api.example.com/users', {
33
- method: 'POST',
47
+ const response = await fetch("https://api.example.com/users", {
48
+ method: "POST",
34
49
  headers: {
35
- 'Content-Type': 'application/json',
36
- 'Authorization': 'Basic ' + btoa('username:password')
50
+ "Content-Type": "application/json",
51
+ Authorization: "Basic " + btoa("username:password"),
37
52
  },
38
- body: JSON.stringify(userData)
53
+ body: JSON.stringify(userData),
39
54
  });
40
55
 
41
56
  if (!response.ok) {
@@ -44,22 +59,26 @@ async function createUser(userData) {
44
59
  const data = await response.json();
45
60
  return data;
46
61
  } catch (error) {
47
- console.error('Fetch error:', error);
62
+ console.error("Fetch error:", error);
48
63
  throw error;
49
64
  }
50
65
  }
66
+ ```
51
67
 
52
- // After: With create-request
53
- import create from 'create-request';
68
+ ### With create-request
69
+
70
+ ```typescript
71
+ import create from "create-request";
54
72
 
55
73
  function createUser(userData) {
56
- return create.post()
57
- .withBasicAuth('username', 'password')
58
- .withBody(userData)
59
- .sendTo('https://api.example.com/users')
60
- .getData<User>()
74
+ return create
75
+ .post()
76
+ .withBasicAuth("username", "password")
77
+ .withBody(userData) // Content-Type automatically set to application/json
78
+ .sendTo("https://api.example.com/users")
79
+ .getData<User>() // Type-safe response handling
61
80
  .catch(error => {
62
- console.error('Fetch error:', error);
81
+ console.error("Fetch error:", error);
63
82
  throw error;
64
83
  });
65
84
  }
@@ -68,13 +87,14 @@ function createUser(userData) {
68
87
  ## Installation
69
88
 
70
89
  ```bash
90
+ # npm
71
91
  npm install create-request
72
- ```
73
92
 
74
- Or with yarn:
75
-
76
- ```bash
93
+ # yarn
77
94
  yarn add create-request
95
+
96
+ # pnpm
97
+ pnpm add create-request
78
98
  ```
79
99
 
80
100
  ## Basic Usage
@@ -82,16 +102,16 @@ yarn add create-request
82
102
  ### Creating Requests
83
103
 
84
104
  ```typescript
85
- import create from 'create-request';
105
+ import create from "create-request";
86
106
 
87
107
  // Create different request types
88
- const getRequest = create.get();
89
- const postRequest = create.post();
90
- const putRequest = create.put();
91
- const headRequest = create.head();
92
- const deleteRequest = create.del();
93
- const patchRequest = create.patch();
94
- 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
95
115
  ```
96
116
 
97
117
  ### Request Configuration
@@ -99,13 +119,14 @@ const optionsRequest = create.options();
99
119
  The library provides a comprehensive set of configuration methods that can be chained together to customize your requests:
100
120
 
101
121
  ```typescript
102
- import create, { RequestPriority, CredentialsPolicy, RequestMode, CacheMode, RedirectMode, ReferrerPolicy } from 'create-request';
122
+ import create, { RequestPriority, CredentialsPolicy, RequestMode, RedirectMode, ReferrerPolicy } from "create-request";
103
123
 
104
124
  // Configure request options
105
- const request = create.get()
125
+ const request = create
126
+ .get()
106
127
  // Basic headers
107
- .withHeaders({ 'X-API-Key': 'abc123', 'Accept-Language': 'en-US' })
108
- .withHeader('Custom-Header', 'value') // Add a single header
128
+ .withHeaders({ "X-API-Key": "abc123", "Accept-Language": "en-US" })
129
+ .withHeader("Custom-Header", "value") // Add a single header
109
130
 
110
131
  // Timeout settings
111
132
  .withTimeout(5000) // Request will abort after 5 seconds
@@ -114,141 +135,101 @@ const request = create.get()
114
135
  .withRetries(3) // Retry up to 3 times on failure
115
136
  .onRetry(({ attempt, error }) => {
116
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));
117
140
  })
118
141
 
119
142
  // Authentication methods
120
- .withBearerToken('your-token') // Adds Authorization: Bearer your-token
121
- .withBasicAuth('username', 'password') // HTTP Basic Authentication
143
+ .withBearerToken("your-token") // Adds Authorization: Bearer your-token
144
+ .withBasicAuth("username", "password") // HTTP Basic Authentication
122
145
 
123
146
  // URL parameters
124
- .withQueryParams({ search: 'term', page: 1, limit: 20 })
125
- .withQueryParam('filter', 'active') // Add a single query parameter
147
+ .withQueryParams({ search: "term", page: 1, limit: 20 })
148
+ .withQueryParam("filter", "active") // Add a single query parameter
126
149
 
127
150
  // Request body configuration (for POST/PUT/PATCH)
128
- .withContentType('application/json') // Set specific content type
151
+ .withContentType("application/json") // Set specific content type
129
152
 
130
153
  // Fetch API options
131
154
  .withCredentials(CredentialsPolicy.INCLUDE) // Includes cookies with cross-origin requests
132
155
  .withMode(RequestMode.CORS) // Controls CORS behavior
133
156
  .withRedirect(RedirectMode.FOLLOW) // Controls redirect behavior (follow, error, manual)
134
- .withReferrer('https://example.com') // Sets request referrer
157
+ .withReferrer("https://example.com") // Sets request referrer
135
158
  .withReferrerPolicy(ReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE) // Controls referrer policy
136
- .withPriority(RequestPriority.HIGH) // Sets request priority
159
+ .withPriority(RequestPriority.HIGH); // Sets request priority
137
160
  ```
138
161
 
139
162
  Each configuration method returns the request object, allowing for a fluent interface where methods can be chained together. You can configure only what you need for a specific request:
140
163
 
141
164
  ```typescript
142
165
  // Simple example with just what's needed
143
- const searchUsers = create.get()
144
- .withBearerToken(userToken)
145
- .withQueryParams({ q: searchTerm, limit: 20 })
146
- .withTimeout(3000);
166
+ const searchUsers = create.get().withBearerToken(userToken).withQueryParams({ q: searchTerm, limit: 20 }).withTimeout(3000);
147
167
 
148
168
  // Now execute the request
149
- const users = await searchUsers.sendTo('https://api.example.com/users').getData();
169
+ const users = await searchUsers.sendTo("https://api.example.com/users").getData();
150
170
  ```
151
171
 
152
172
  You can also create reusable base requests with common settings:
153
173
 
154
174
  ```typescript
155
175
  // Create base authenticated request
156
- const apiBase = create.get()
176
+ const apiBase = create
177
+ .get()
157
178
  .withHeaders({
158
- 'X-API-Version': '1.2',
159
- 'Accept-Language': 'en-US'
179
+ "X-API-Version": "1.2",
180
+ "Accept-Language": "en-US",
160
181
  })
161
182
  .withBearerToken(authToken)
162
183
  .withTimeout(5000)
163
184
  .withRetries(2);
164
185
 
165
186
  // Use the base request for different endpoints
166
- const users = await apiBase.sendTo('https://api.example.com/users').getData();
167
- const products = await apiBase.sendTo('https://api.example.com/products').getData();
187
+ const users = await apiBase.sendTo("https://api.example.com/users").getData();
188
+ const products = await apiBase.sendTo("https://api.example.com/products").getData();
168
189
  ```
169
190
 
170
191
  ### Request Bodies (POST/PUT/PATCH)
171
192
 
172
193
  ```typescript
173
- // JSON body
174
- const jsonRequest = create.post()
175
- .withBody({ name: 'John', age: 30 });
194
+ // JSON body (Content-Type automatically set to application/json)
195
+ const jsonRequest = create.post().withBody({ name: "John", age: 30 });
176
196
 
177
- // String body
178
- const textRequest = create.post()
179
- .withBody('Plain text content');
197
+ // String body (Content-Type automatically set to text/plain)
198
+ const textRequest = create.post().withBody("Plain text content");
180
199
 
181
200
  // Form data
182
201
  const formData = new FormData();
183
- formData.append('name', 'John');
184
- formData.append('file', fileBlob);
202
+ formData.append("name", "John");
203
+ formData.append("file", fileBlob);
185
204
 
186
- const formRequest = create.post()
187
- .withBody(formData);
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);
188
213
  ```
189
214
 
190
215
  ### Executing Requests
191
216
 
192
217
  ```typescript
193
218
  // Simple execution
194
- const response = await request.sendTo('https://api.example.com/endpoint');
219
+ const response = await request.sendTo("https://api.example.com/endpoint");
195
220
 
196
221
  // With direct data extraction
197
- const jsonData = await request.sendTo('https://api.example.com/endpoint').getJson();
198
- const textData = await request.sendTo('https://api.example.com/endpoint').getText();
199
- const blobData = await request.sendTo('https://api.example.com/endpoint').getBlob();
200
- const bodyStream = await request.sendTo('https://api.example.com/endpoint').getBody();
201
- const arrayBuffer = await request.sendTo('https://api.example.com/endpoint').getArrayBuffer();
222
+ const jsonData = await request.sendTo("https://api.example.com/endpoint").getJson();
223
+ const textData = await request.sendTo("https://api.example.com/endpoint").getText();
224
+ const blobData = await request.sendTo("https://api.example.com/endpoint").getBlob();
225
+ const bodyStream = await request.sendTo("https://api.example.com/endpoint").getBody();
226
+ const arrayBuffer = await request.sendTo("https://api.example.com/endpoint").getArrayBuffer();
202
227
 
203
228
  // Using the data selector API to extract specific data
204
- const userData = await request.sendTo('https://api.example.com/users')
205
- .getData(data => data.results.users);
229
+ const userData = await request.sendTo("https://api.example.com/users").getData(data => data.results.users);
206
230
 
207
231
  // Using the data selector without a selector function just returns the full JSON response
208
- const fullData = await request.sendTo('https://api.example.com/data').getData();
209
- ```
210
-
211
- ### Data Selection
212
-
213
- The `getData` method provides a powerful way to extract and transform specific data from API responses:
214
-
215
- ```typescript
216
- // Extract specific properties from nested structures
217
- const posts = await request
218
- .sendTo('https://api.example.com/feed')
219
- .getData(data => data.feed.posts);
220
-
221
- // Transform data in the selector function
222
- const usernames = await request
223
- .sendTo('https://api.example.com/users')
224
- .getData(data => data.users.map(user => user.username));
225
-
226
- // Apply filtering in the selector
227
- const activeUsers = await request
228
- .sendTo('https://api.example.com/users')
229
- .getData(data => data.users.filter(user => user.isActive));
230
-
231
- // Combine data from complex nested structures
232
- const combinedData = await request
233
- .sendTo('https://api.example.com/dashboard')
234
- .getData(data => ({
235
- userCount: data.stats.users.total,
236
- recentPosts: data.content.recent.slice(0, 5),
237
- notifications: data.user.notifications.unread
238
- }));
239
- ```
240
-
241
- When a selector fails, the error message will contain the original response data to help diagnose the issue:
242
-
243
- ```typescript
244
- try {
245
- const result = await request
246
- .sendTo('https://api.example.com/data')
247
- .getData(data => data.results.items); // Will fail if structure is different
248
- } catch (error) {
249
- console.error(error);
250
- // Error message includes the original response data
251
- }
232
+ const fullData = await request.sendTo("https://api.example.com/data").getData();
252
233
  ```
253
234
 
254
235
  ### Error Handling
@@ -257,13 +238,13 @@ All errors from requests are instances of `RequestError` with detailed informati
257
238
 
258
239
  ```typescript
259
240
  try {
260
- const data = await request.sendTo('https://api.example.com/data').getJson();
241
+ const data = await request.sendTo("https://api.example.com/data").getJson();
261
242
  } catch (error) {
262
243
  // error will always be a RequestError
263
- console.log(error.message); // Error message
264
- console.log(error.status); // HTTP status code (if available)
265
- console.log(error.url); // Request URL
266
- console.log(error.method); // HTTP method
244
+ console.log(error.message); // Error message
245
+ console.log(error.status); // HTTP status code (if available)
246
+ console.log(error.url); // Request URL
247
+ console.log(error.method); // HTTP method
267
248
  console.log(error.timeoutError); // Whether it was a timeout
268
249
 
269
250
  // Access the original response if available
@@ -277,34 +258,37 @@ try {
277
258
  ### Caching Requests
278
259
 
279
260
  ```typescript
280
- import create, { createMemoryStorage, createLocalStorageStorage, createSessionStorageStorage } from 'create-request';
261
+ import create, { createMemoryStorage, createLocalStorageStorage, createSessionStorageStorage } from "create-request";
281
262
 
282
263
  // Simple in-memory caching
283
- const request = create.get()
264
+ const request = create
265
+ .get()
284
266
  .withCache({
285
267
  storage: createMemoryStorage(),
286
- ttl: 60000 // 1 minute in milliseconds
268
+ ttl: 60000, // 1 minute in milliseconds
287
269
  })
288
- .sendTo('https://api.example.com/data');
270
+ .sendTo("https://api.example.com/data");
289
271
 
290
272
  // Using localStorage storage
291
- const request = create.get()
273
+ const request = create
274
+ .get()
292
275
  .withCache({
293
276
  storage: createLocalStorageStorage(),
294
277
  ttl: 5 * 60 * 1000, // 5 minutes
295
- maxSize: '1MB',
296
- keyPrefix: 'user-data'
278
+ maxSize: "1MB",
279
+ keyPrefix: "user-data",
297
280
  })
298
- .sendTo('https://api.example.com/users');
281
+ .sendTo("https://api.example.com/users");
299
282
 
300
283
  // Using sessionStorage for per-session caching
301
- const request = create.get()
284
+ const request = create
285
+ .get()
302
286
  .withCache({
303
287
  storage: createSessionStorageStorage(),
304
288
  maxEntries: 50,
305
- varyByHeaders: ['x-api-version']
289
+ varyByHeaders: ["x-api-version"],
306
290
  })
307
- .sendTo('https://api.example.com/data');
291
+ .sendTo("https://api.example.com/data");
308
292
  ```
309
293
 
310
294
  ## Advanced Usage
@@ -313,14 +297,31 @@ const request = create.get()
313
297
 
314
298
  ```typescript
315
299
  // Create a base authenticated request
316
- const authRequest = create.get()
317
- .withBearerToken('token123')
318
- .withTimeout(5000)
319
- .withRetries(2);
300
+ const authRequest = create.get().withBearerToken("token123").withTimeout(5000).withRetries(2);
320
301
 
321
302
  // Reuse for different endpoints
322
- const users = await authRequest.sendTo('https://api.example.com/users').getJson();
323
- const products = await authRequest.sendTo('https://api.example.com/products').getJson();
303
+ const users = await authRequest.sendTo("https://api.example.com/users").getJson();
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
+ }
324
325
  ```
325
326
 
326
327
  ### Request Cancellation
@@ -328,95 +329,56 @@ const products = await authRequest.sendTo('https://api.example.com/products').ge
328
329
  ```typescript
329
330
  const controller = new AbortController();
330
331
 
331
- const request = create.get()
332
- .withTimeout(10000)
333
- .withAbortController(controller);
332
+ const request = create.get().withTimeout(10000).withAbortController(controller);
334
333
 
335
334
  // Later, cancel the request if needed
336
335
  setTimeout(() => controller.abort(), 2000);
337
336
 
338
337
  try {
339
- const data = await request.sendTo('https://api.example.com/slow-endpoint').getJson();
338
+ const data = await request.sendTo("https://api.example.com/slow-endpoint").getJson();
340
339
  } catch (error) {
341
- // Check if request was cancelled
342
- console.log('Request was cancelled:', error.name === 'AbortError');
343
- }
344
- ```
345
-
346
- ### Working with Typed Responses
347
-
348
- ```typescript
349
- interface User {
350
- id: number;
351
- name: string;
352
- email: string;
353
- }
354
-
355
- interface ApiResponse {
356
- users: User[];
357
- pagination: {
358
- total: number;
359
- page: number;
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);
360
346
  }
361
347
  }
348
+ ```
362
349
 
363
- const userRequest = create.get();
364
-
365
- // Type the full response
366
- const response = await userRequest
367
- .sendTo('https://api.example.com/users')
368
- .getJson<ApiResponse>();
369
-
370
- // Or use getData with type parameters
371
- const users = await userRequest
372
- .sendTo('https://api.example.com/users')
373
- .getData<ApiResponse, User[]>(data => data.users);
350
+ ### Data Selection
374
351
 
375
- // Or just get the full typed response
376
- const fullData = await userRequest
377
- .sendTo('https://api.example.com/users')
378
- .getData<ApiResponse>();
352
+ The `getData` method provides a powerful way to extract and transform specific data from API responses:
379
353
 
380
- // TypeScript knows the types
381
- console.log(users[0].name);
382
- ```
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);
383
357
 
384
- ### 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));
385
360
 
386
- ```typescript
387
- 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));
388
363
 
389
- request.sendTo('https://api.example.com/data')
390
- .then(response => {
391
- console.log('Status:', response.status);
392
- return response.getJson();
393
- })
394
- .then(data => {
395
- console.log('Data:', data);
396
- })
397
- .catch(error => {
398
- console.error('Error:', error.message);
399
- });
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
+ }));
400
370
  ```
401
371
 
402
- ### Advanced Caching
372
+ When a selector fails, the error message will contain the original response data to help diagnose the issue:
403
373
 
404
374
  ```typescript
405
- import create, { createMemoryStorage, StorageProvider } from 'create-request';
406
-
407
- // Custom key generation
408
- const request = create.get()
409
- .withCache({
410
- storage: createMemoryStorage(),
411
- keyGenerator: (url, method, headers) => {
412
- // Include user ID from authorization header in the cache key
413
- const authHeader = headers?.['Authorization'] || '';
414
- const userMatch = authHeader.match(/User-(\d+)/);
415
- const userId = userMatch ? userMatch[1] : 'anonymous';
416
- return `${method}:${userId}:${url}`;
417
- }
418
- })
419
- .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
+ }
420
382
  ```
421
383
 
422
384
  ### Implementing Custom Storage Providers
@@ -424,9 +386,9 @@ const request = create.get()
424
386
  You can create your own storage provider by implementing the `StorageProvider` interface:
425
387
 
426
388
  ```typescript
427
- import { StorageProvider } from 'create-request';
389
+ import { StorageProvider } from "create-request";
428
390
 
429
- // Create a simple custom storage provider
391
+ // Create a namespaced storage provider
430
392
  export function createNamespacedStorage(namespace: string): StorageProvider {
431
393
  return {
432
394
  get(key: string): string | null {
@@ -461,26 +423,65 @@ export function createNamespacedStorage(namespace: string): StorageProvider {
461
423
  Object.keys(localStorage)
462
424
  .filter(key => key.startsWith(`${namespace}:`))
463
425
  .forEach(key => localStorage.removeItem(key));
464
- }
426
+ },
465
427
  };
466
428
  }
467
429
 
468
430
  // Usage:
469
- const request = create.get()
431
+ const request = create
432
+ .get()
470
433
  .withCache({
471
- storage: createNamespacedStorage('api-cache'),
472
- ttl: 24 * 60 * 60 * 1000 // 24 hours
434
+ storage: createNamespacedStorage("api-cache"),
435
+ ttl: 24 * 60 * 60 * 1000, // 24 hours
473
436
  })
474
- .sendTo('https://api.example.com/data');
437
+ .sendTo("https://api.example.com/data");
475
438
  ```
476
439
 
477
- 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[]>>();
478
460
 
479
- - Custom browser storage solutions
480
- - Memory caches with expiration policies
481
- - Session-based storage mechanisms
482
- - Encrypted storage providers
483
- - Third-party storage libraries
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);
466
+
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
+ ```
484
485
 
485
486
  ## CSRF Protection
486
487
 
@@ -502,9 +503,9 @@ You can configure CSRF settings globally for all requests:
502
503
 
503
504
  ```typescript
504
505
  // Configure CSRF settings for all requests
505
- create.config.setCsrfToken('your-csrf-token');
506
- create.config.setCsrfHeaderName('X-CSRF-Token'); // Default header name
507
- create.config.setXsrfCookieName('XSRF-TOKEN'); // Default cookie name
506
+ create.config.setCsrfToken("your-csrf-token");
507
+ create.config.setCsrfHeaderName("X-CSRF-Token"); // Default header name
508
+ create.config.setXsrfCookieName("XSRF-TOKEN"); // Default cookie name
508
509
  create.config.setEnableAntiCsrf(true); // Enable/disable X-Requested-With header
509
510
  create.config.setEnableAutoXsrf(true); // Enable/disable automatic cookie-to-header token
510
511
  ```
@@ -515,8 +516,9 @@ You can also configure CSRF protection on individual requests:
515
516
 
516
517
  ```typescript
517
518
  // Configure CSRF for a specific request
518
- const request = create.post()
519
- .withCsrfToken('request-specific-token') // Set a specific token
519
+ const request = create
520
+ .post()
521
+ .withCsrfToken("request-specific-token") // Set a specific token
520
522
  .withAntiCsrfHeaders() // Explicitly add X-Requested-With header
521
523
  .withoutCsrfProtection(); // Or disable all automatic CSRF protection
522
524
  ```
@@ -530,7 +532,15 @@ Most modern frameworks support CSRF protection out of the box. The library works
530
532
  - **Django**: Compatible with Django's CSRF middleware
531
533
  - **Express.js + csurf**: Works with the csurf middleware token pattern
532
534
 
533
- 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
534
544
 
535
545
  ## Browser Support
536
546
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-request",
3
- "version": "0.0.1-beta.13",
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",