create-request 0.0.1-beta.8 → 1.0.0
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 +538 -331
- package/dist/library/BaseRequest.d.ts +275 -41
- package/dist/library/BodyRequest.d.ts +27 -4
- package/dist/library/RequestError.d.ts +5 -2
- package/dist/library/ResponseWrapper.d.ts +66 -13
- package/dist/library/enums.d.ts +0 -11
- package/dist/library/index.cjs +2109 -0
- package/dist/library/index.cjs.map +1 -0
- package/dist/library/index.d.ts +12 -12
- package/dist/library/index.esm.js +1322 -723
- package/dist/library/index.esm.js.map +1 -0
- package/dist/library/index.esm.min.js +1 -0
- package/dist/library/index.esm.min.js.map +1 -0
- package/dist/library/index.min.cjs +1 -0
- package/dist/library/index.min.cjs.map +1 -0
- package/dist/library/requestFactories.d.ts +70 -9
- package/dist/library/requestMethods.d.ts +70 -3
- package/dist/library/types.d.ts +44 -5
- package/dist/library/utils/Config.d.ts +164 -12
- package/dist/library/utils/CookieUtils.d.ts +1 -1
- package/dist/library/utils/sizeUtils.d.ts +8 -0
- package/package.json +74 -16
- package/dist/library/index.js +0 -1522
- package/dist/library/types/cache.d.ts +0 -62
- package/dist/library/utils/CacheManager.d.ts +0 -35
- package/dist/library/utils/CacheStorage.d.ts +0 -57
- package/dist/library/utils/StorageProvider.d.ts +0 -33
package/README.md
CHANGED
|
@@ -3,36 +3,58 @@
|
|
|
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
|
|
|
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
|
+
- [GraphQL Support](#graphql-support)
|
|
18
|
+
- [Interceptors](#interceptors)
|
|
19
|
+
- [TypeScript Support](#typescript-support)
|
|
20
|
+
- [CSRF Protection](#csrf-protection)
|
|
21
|
+
- [Performance Considerations](#performance-considerations)
|
|
22
|
+
- [Browser Support](#browser-support)
|
|
23
|
+
- [Comparison of JavaScript HTTP Client Libraries](#comparison-of-javascript-http-client-libraries)
|
|
24
|
+
- [License](#license)
|
|
25
|
+
|
|
9
26
|
## Core Features
|
|
10
27
|
|
|
11
|
-
- ⏱️ **Timeout Support** - Set timeouts for any request
|
|
12
28
|
- 🚀 **Performance** - Tiny bundle size with zero dependencies
|
|
13
29
|
- 🚧 **Error Handling** - Detailed error info with custom error class
|
|
14
|
-
- 🔐 **Auth Helpers** - Simple methods for common authentication patterns
|
|
15
30
|
- ⛓️ **Chainable API** - Build and execute requests with a fluent interface
|
|
16
|
-
-
|
|
17
|
-
- 🔁 **Automatic Retries** - Retry failed requests with customizable settings
|
|
31
|
+
- ⏱️ **Timeout Support** - Set timeouts for requests with automatic aborts
|
|
18
32
|
- 🛡️ **Type Safety** - Full TypeScript support with intelligent type inference
|
|
19
|
-
-
|
|
33
|
+
- 🔐 **Auth Helpers** - Simple methods for common authentication patterns
|
|
34
|
+
- 🔍 **Data Selection** - Extract and transform specific data from responses
|
|
35
|
+
- 🔁 **Automatic Retries** - Retry failed requests with customizable settings
|
|
36
|
+
- 📉 **Reduced Boilerplate** - Write 60% less code for common API operations
|
|
37
|
+
- 🔒 **CSRF Protection** - Built-in safeguards against cross-site request forgery
|
|
38
|
+
- 🛑 **Request Cancellation** - Abort requests on demand with AbortController integration
|
|
39
|
+
- 🔌 **Interceptors** - Global and per-request interceptors for requests, responses, and errors
|
|
40
|
+
- 🔷 **GraphQL Support** - Built-in GraphQL query and mutation helpers
|
|
20
41
|
|
|
21
42
|
## Why create-request?
|
|
22
43
|
|
|
23
44
|
**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:
|
|
24
45
|
|
|
46
|
+
### With Regular Fetch
|
|
47
|
+
|
|
25
48
|
```typescript
|
|
26
|
-
// Before: Regular fetch
|
|
27
49
|
async function createUser(userData) {
|
|
28
50
|
try {
|
|
29
|
-
const response = await fetch(
|
|
30
|
-
method:
|
|
51
|
+
const response = await fetch("https://api.example.com/users", {
|
|
52
|
+
method: "POST",
|
|
31
53
|
headers: {
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
Authorization: "Basic " + btoa("username:password"),
|
|
34
56
|
},
|
|
35
|
-
body: JSON.stringify(userData)
|
|
57
|
+
body: JSON.stringify(userData),
|
|
36
58
|
});
|
|
37
59
|
|
|
38
60
|
if (!response.ok) {
|
|
@@ -41,22 +63,25 @@ async function createUser(userData) {
|
|
|
41
63
|
const data = await response.json();
|
|
42
64
|
return data;
|
|
43
65
|
} catch (error) {
|
|
44
|
-
console.error(
|
|
66
|
+
console.error("Fetch error:", error);
|
|
45
67
|
throw error;
|
|
46
68
|
}
|
|
47
69
|
}
|
|
70
|
+
```
|
|
48
71
|
|
|
49
|
-
|
|
50
|
-
|
|
72
|
+
### With create-request
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import create from "create-request";
|
|
51
76
|
|
|
52
77
|
function createUser(userData) {
|
|
53
|
-
return create
|
|
54
|
-
.
|
|
55
|
-
.
|
|
56
|
-
.
|
|
57
|
-
.getData<User>()
|
|
78
|
+
return create
|
|
79
|
+
.post("https://api.example.com/users")
|
|
80
|
+
.withBasicAuth("username", "password")
|
|
81
|
+
.withBody(userData) // Content-Type automatically set to application/json
|
|
82
|
+
.getData<User>() // Type-safe response handling
|
|
58
83
|
.catch(error => {
|
|
59
|
-
console.error(
|
|
84
|
+
console.error("Fetch error:", error);
|
|
60
85
|
throw error;
|
|
61
86
|
});
|
|
62
87
|
}
|
|
@@ -65,13 +90,14 @@ function createUser(userData) {
|
|
|
65
90
|
## Installation
|
|
66
91
|
|
|
67
92
|
```bash
|
|
93
|
+
# npm
|
|
68
94
|
npm install create-request
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Or with yarn:
|
|
72
95
|
|
|
73
|
-
|
|
96
|
+
# yarn
|
|
74
97
|
yarn add create-request
|
|
98
|
+
|
|
99
|
+
# pnpm
|
|
100
|
+
pnpm add create-request
|
|
75
101
|
```
|
|
76
102
|
|
|
77
103
|
## Basic Usage
|
|
@@ -79,16 +105,16 @@ yarn add create-request
|
|
|
79
105
|
### Creating Requests
|
|
80
106
|
|
|
81
107
|
```typescript
|
|
82
|
-
import create from
|
|
83
|
-
|
|
84
|
-
// Create different request types
|
|
85
|
-
const getRequest = create.get();
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
const headRequest = create.head();
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const optionsRequest = create.options();
|
|
108
|
+
import create from "create-request";
|
|
109
|
+
|
|
110
|
+
// Create different request types with URL
|
|
111
|
+
const getRequest = create.get("https://api.example.com/users"); // GET
|
|
112
|
+
const putRequest = create.put("https://api.example.com/users/1"); // PUT
|
|
113
|
+
const postRequest = create.post("https://api.example.com/users"); // POST
|
|
114
|
+
const headRequest = create.head("https://api.example.com/users/1"); // HEAD
|
|
115
|
+
const patchRequest = create.patch("https://api.example.com/users/1"); // PATCH
|
|
116
|
+
const deleteRequest = create.del("https://api.example.com/users/1"); // DELETE
|
|
117
|
+
const optionsRequest = create.options("https://api.example.com/users"); // OPTIONS
|
|
92
118
|
```
|
|
93
119
|
|
|
94
120
|
### Request Configuration
|
|
@@ -96,161 +122,214 @@ const optionsRequest = create.options();
|
|
|
96
122
|
The library provides a comprehensive set of configuration methods that can be chained together to customize your requests:
|
|
97
123
|
|
|
98
124
|
```typescript
|
|
99
|
-
import create, {
|
|
125
|
+
import create, {
|
|
126
|
+
RequestPriority,
|
|
127
|
+
CredentialsPolicy,
|
|
128
|
+
RedirectMode,
|
|
129
|
+
ReferrerPolicy,
|
|
130
|
+
SameSitePolicy,
|
|
131
|
+
} from "create-request";
|
|
100
132
|
|
|
101
133
|
// Configure request options
|
|
102
|
-
const request = create
|
|
134
|
+
const request = create
|
|
135
|
+
.get("https://api.example.com/users")
|
|
103
136
|
// Basic headers
|
|
104
|
-
.withHeaders({
|
|
105
|
-
.withHeader(
|
|
137
|
+
.withHeaders({ "X-API-Key": "abc123", "Accept-Language": "en-US" })
|
|
138
|
+
.withHeader("Custom-Header", "value") // Add a single header
|
|
106
139
|
|
|
107
140
|
// Timeout settings
|
|
108
141
|
.withTimeout(5000) // Request will abort after 5 seconds
|
|
109
142
|
|
|
110
143
|
// Automatic retry configuration
|
|
111
144
|
.withRetries(3) // Retry up to 3 times on failure
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
.onRetry(({ attempt, error, delay }) => {
|
|
115
|
-
console.log(`Attempt ${attempt} failed: ${error.message}. Retrying in ${delay}ms`);
|
|
145
|
+
.onRetry(({ attempt, error }) => {
|
|
146
|
+
console.log(`Attempt ${attempt} failed: ${error.message}. Retrying...`);
|
|
116
147
|
})
|
|
117
148
|
|
|
118
149
|
// Authentication methods
|
|
119
|
-
.withBearerToken(
|
|
120
|
-
.withBasicAuth(
|
|
150
|
+
.withBearerToken("your-token") // Adds Authorization: Bearer your-token
|
|
151
|
+
.withBasicAuth("username", "password") // HTTP Basic Authentication
|
|
152
|
+
.withAuthorization("auth-scheme value") // Custom authorization header
|
|
153
|
+
|
|
154
|
+
// Add a single cookie
|
|
155
|
+
.withCookie("language", "en-US")
|
|
156
|
+
|
|
157
|
+
// Add multiple cookies
|
|
158
|
+
.withCookies({
|
|
159
|
+
sessionId: "abc123",
|
|
160
|
+
preferences: { value: "dark-mode", secure: true },
|
|
161
|
+
tracking: { value: "enabled", sameSite: SameSitePolicy.STRICT },
|
|
162
|
+
})
|
|
121
163
|
|
|
122
|
-
// URL parameters
|
|
123
|
-
.withQueryParams({ search:
|
|
124
|
-
.withQueryParam(
|
|
164
|
+
// URL parameters (supports arrays, null/undefined filtering, and all types)
|
|
165
|
+
.withQueryParams({ search: "term", page: 1, limit: 20, tags: ["js", "ts"] })
|
|
166
|
+
.withQueryParam("filter", "active") // Add a single query parameter
|
|
167
|
+
.withQueryParam("ids", [1, 2, 3]) // Array values create multiple query params
|
|
125
168
|
|
|
126
169
|
// Request body configuration (for POST/PUT/PATCH)
|
|
127
|
-
.withContentType(
|
|
170
|
+
.withContentType("application/json") // Set specific content type
|
|
128
171
|
|
|
129
172
|
// Fetch API options
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
.
|
|
133
|
-
.withRedirect
|
|
134
|
-
.withReferrer(
|
|
135
|
-
.withReferrerPolicy
|
|
136
|
-
.withPriority
|
|
173
|
+
// Note: These methods support three styles - Fluent API (shown below), Enum-based (e.g., .withMode(RequestMode.CORS)), or String-based (e.g., .withMode("cors"))
|
|
174
|
+
.withCredentials.INCLUDE() // Includes cookies with cross-origin requests
|
|
175
|
+
.withMode.CORS() // Controls CORS behavior
|
|
176
|
+
.withRedirect.FOLLOW() // Controls redirect behavior (follow, error, manual)
|
|
177
|
+
.withReferrer("https://example.com") // Sets request referrer
|
|
178
|
+
.withReferrerPolicy.NO_REFERRER_WHEN_DOWNGRADE() // Controls referrer policy
|
|
179
|
+
.withPriority.HIGH() // Sets request priority
|
|
180
|
+
.withKeepAlive(true); // Keeps connection alive after the page is unloaded
|
|
137
181
|
```
|
|
138
182
|
|
|
139
183
|
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
184
|
|
|
141
185
|
```typescript
|
|
142
186
|
// Simple example with just what's needed
|
|
143
|
-
const
|
|
187
|
+
const users = await create
|
|
188
|
+
.get("https://api.example.com/users")
|
|
144
189
|
.withBearerToken(userToken)
|
|
145
190
|
.withQueryParams({ q: searchTerm, limit: 20 })
|
|
146
|
-
.withTimeout(3000)
|
|
147
|
-
|
|
148
|
-
// Now execute the request
|
|
149
|
-
const users = await searchUsers.sendTo('https://api.example.com/users').getData();
|
|
191
|
+
.withTimeout(3000)
|
|
192
|
+
.getData();
|
|
150
193
|
```
|
|
151
194
|
|
|
152
|
-
|
|
195
|
+
### Request Bodies (POST/PUT/PATCH)
|
|
153
196
|
|
|
154
197
|
```typescript
|
|
155
|
-
//
|
|
156
|
-
const
|
|
157
|
-
.
|
|
158
|
-
|
|
159
|
-
'Accept-Language': 'en-US'
|
|
160
|
-
})
|
|
161
|
-
.withBearerToken(authToken)
|
|
162
|
-
.withTimeout(5000)
|
|
163
|
-
.withRetries(2);
|
|
198
|
+
// JSON body (Content-Type automatically set to application/json)
|
|
199
|
+
const jsonRequest = create
|
|
200
|
+
.post("https://api.example.com/users")
|
|
201
|
+
.withBody({ name: "John", age: 30 });
|
|
164
202
|
|
|
165
|
-
//
|
|
166
|
-
const
|
|
167
|
-
|
|
203
|
+
// String body (Content-Type automatically set to text/plain)
|
|
204
|
+
const textRequest = create
|
|
205
|
+
.post("https://api.example.com/users")
|
|
206
|
+
.withBody("Plain text content");
|
|
207
|
+
|
|
208
|
+
// Form data
|
|
209
|
+
const formData = new FormData();
|
|
210
|
+
formData.append("name", "John");
|
|
211
|
+
formData.append("file", fileBlob);
|
|
212
|
+
|
|
213
|
+
const formRequest = create.post("https://api.example.com/users").withBody(formData);
|
|
214
|
+
|
|
215
|
+
// URLSearchParams (typically used for application/x-www-form-urlencoded)
|
|
216
|
+
const params = new URLSearchParams();
|
|
217
|
+
params.append("username", "john");
|
|
218
|
+
params.append("password", "secret");
|
|
219
|
+
|
|
220
|
+
const formUrlEncodedRequest = create.post("https://api.example.com/login").withBody(params);
|
|
168
221
|
```
|
|
169
222
|
|
|
170
|
-
###
|
|
223
|
+
### GraphQL Requests
|
|
224
|
+
|
|
225
|
+
The library provides built-in support for GraphQL queries and mutations:
|
|
171
226
|
|
|
172
227
|
```typescript
|
|
173
|
-
//
|
|
174
|
-
const
|
|
175
|
-
.
|
|
228
|
+
// GraphQL query without variables
|
|
229
|
+
const users = await create
|
|
230
|
+
.post("https://api.example.com/graphql")
|
|
231
|
+
.withGraphQL("query { users { id name email } }")
|
|
232
|
+
.getJson();
|
|
233
|
+
|
|
234
|
+
// GraphQL query with variables
|
|
235
|
+
const user = await create
|
|
236
|
+
.post("https://api.example.com/graphql")
|
|
237
|
+
.withGraphQL("query GetUser($id: ID!) { user(id: $id) { name email } }", { id: "123" })
|
|
238
|
+
.getJson();
|
|
239
|
+
|
|
240
|
+
// GraphQL mutation
|
|
241
|
+
const result = await create
|
|
242
|
+
.post("https://api.example.com/graphql")
|
|
243
|
+
.withGraphQL(
|
|
244
|
+
"mutation CreateUser($name: String!) { createUser(name: $name) { id name } }",
|
|
245
|
+
{ name: "John Doe" }
|
|
246
|
+
)
|
|
247
|
+
.getJson();
|
|
248
|
+
```
|
|
176
249
|
|
|
177
|
-
|
|
178
|
-
const textRequest = create.post()
|
|
179
|
-
.withBody('Plain text content');
|
|
250
|
+
The `withGraphQL` method automatically:
|
|
180
251
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
252
|
+
- Formats the body as JSON with `query` and optional `variables` properties
|
|
253
|
+
- Sets `Content-Type` to `application/json`
|
|
254
|
+
- Trims whitespace from the query string
|
|
255
|
+
- Validates the query is non-empty
|
|
256
|
+
- Validates variables are a plain object (not arrays or null)
|
|
257
|
+
|
|
258
|
+
### Query Parameters Advanced Features
|
|
259
|
+
|
|
260
|
+
The library supports advanced query parameter handling:
|
|
185
261
|
|
|
186
|
-
|
|
187
|
-
|
|
262
|
+
```typescript
|
|
263
|
+
// Array values create multiple query params with the same key
|
|
264
|
+
const request = create.get("https://api.example.com/search").withQueryParams({
|
|
265
|
+
tags: ["javascript", "typescript", "node"], // ?tags=javascript&tags=typescript&tags=node
|
|
266
|
+
page: 1,
|
|
267
|
+
active: true,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// Null and undefined values are automatically filtered out
|
|
271
|
+
const filtered = create.get("https://api.example.com/users").withQueryParams({
|
|
272
|
+
name: "John",
|
|
273
|
+
age: null, // Ignored
|
|
274
|
+
email: undefined, // Ignored
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Supports all JavaScript types (strings, numbers, booleans, arrays)
|
|
278
|
+
const typed = create.get("https://api.example.com/data").withQueryParams({
|
|
279
|
+
page: 1, // Number
|
|
280
|
+
active: true, // Boolean
|
|
281
|
+
tags: ["js", "ts"], // Array
|
|
282
|
+
name: "John", // String
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// Merge with existing query params in URL
|
|
286
|
+
const merged = create
|
|
287
|
+
.get("https://api.example.com/users?existing=value")
|
|
288
|
+
.withQueryParams({ new: "param" }); // Both existing and new params included
|
|
188
289
|
```
|
|
189
290
|
|
|
190
291
|
### Executing Requests
|
|
191
292
|
|
|
192
293
|
```typescript
|
|
193
|
-
//
|
|
194
|
-
const response = await
|
|
294
|
+
// Get the full response
|
|
295
|
+
const response = await create.get("https://api.example.com/endpoint").getResponse();
|
|
195
296
|
|
|
196
297
|
// With direct data extraction
|
|
197
|
-
const jsonData = await
|
|
198
|
-
const textData = await
|
|
199
|
-
const blobData = await
|
|
200
|
-
const bodyStream = await
|
|
201
|
-
const arrayBuffer = await request.sendTo('https://api.example.com/endpoint').getArrayBuffer();
|
|
298
|
+
const jsonData = await create.get("https://api.example.com/endpoint").getJson();
|
|
299
|
+
const textData = await create.get("https://api.example.com/endpoint").getText();
|
|
300
|
+
const blobData = await create.get("https://api.example.com/endpoint").getBlob();
|
|
301
|
+
const bodyStream = await create.get("https://api.example.com/endpoint").getBody();
|
|
202
302
|
|
|
203
303
|
// Using the data selector API to extract specific data
|
|
204
|
-
const userData = await
|
|
304
|
+
const userData = await create
|
|
305
|
+
.get("https://api.example.com/users")
|
|
205
306
|
.getData(data => data.results.users);
|
|
206
307
|
|
|
207
308
|
// Using the data selector without a selector function just returns the full JSON response
|
|
208
|
-
const fullData = await
|
|
309
|
+
const fullData = await create.get("https://api.example.com/data").getData();
|
|
209
310
|
```
|
|
210
311
|
|
|
211
|
-
###
|
|
312
|
+
### ResponseWrapper Properties
|
|
212
313
|
|
|
213
|
-
|
|
314
|
+
When you use `getResponse()`, you get a `ResponseWrapper` object that provides convenient access to response properties and methods:
|
|
214
315
|
|
|
215
316
|
```typescript
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
// "Data selector failed: Cannot read properties of undefined (reading 'items').
|
|
252
|
-
// Original data: { "status": "error", "message": "Invalid request" }"
|
|
253
|
-
}
|
|
317
|
+
const response = await create.get("https://api.example.com/users").getResponse();
|
|
318
|
+
|
|
319
|
+
// Access response properties directly
|
|
320
|
+
console.log(response.status); // HTTP status code (e.g., 200)
|
|
321
|
+
console.log(response.statusText); // Status text (e.g., "OK")
|
|
322
|
+
console.log(response.ok); // Boolean: true if status is 200-299
|
|
323
|
+
console.log(response.headers); // Headers object
|
|
324
|
+
console.log(response.url); // Request URL
|
|
325
|
+
console.log(response.method); // HTTP method
|
|
326
|
+
console.log(response.raw); // Raw Response object from fetch
|
|
327
|
+
|
|
328
|
+
// Use wrapper methods for body parsing
|
|
329
|
+
const json = await response.getJson();
|
|
330
|
+
const text = await response.getText();
|
|
331
|
+
const blob = await response.getBlob();
|
|
332
|
+
const stream = response.getBody(); // ReadableStream or null
|
|
254
333
|
```
|
|
255
334
|
|
|
256
335
|
### Error Handling
|
|
@@ -259,14 +338,15 @@ All errors from requests are instances of `RequestError` with detailed informati
|
|
|
259
338
|
|
|
260
339
|
```typescript
|
|
261
340
|
try {
|
|
262
|
-
const data = await
|
|
341
|
+
const data = await create.get("https://api.example.com/data").getJson();
|
|
263
342
|
} catch (error) {
|
|
264
343
|
// error will always be a RequestError
|
|
265
|
-
console.log(error.message);
|
|
266
|
-
console.log(error.status);
|
|
267
|
-
console.log(error.url);
|
|
268
|
-
console.log(error.method);
|
|
269
|
-
console.log(error.
|
|
344
|
+
console.log(error.message); // Error message
|
|
345
|
+
console.log(error.status); // HTTP status code (if available)
|
|
346
|
+
console.log(error.url); // Request URL
|
|
347
|
+
console.log(error.method); // HTTP method
|
|
348
|
+
console.log(error.isTimeout); // Whether it was a timeout
|
|
349
|
+
console.log(error.isAborted); // Whether it was aborted/cancelled
|
|
270
350
|
|
|
271
351
|
// Access the original response if available
|
|
272
352
|
if (error.response) {
|
|
@@ -276,53 +356,186 @@ try {
|
|
|
276
356
|
}
|
|
277
357
|
```
|
|
278
358
|
|
|
279
|
-
|
|
359
|
+
## Advanced Usage
|
|
360
|
+
|
|
361
|
+
### GraphQL Support
|
|
362
|
+
|
|
363
|
+
`create-request` provides first-class support for GraphQL with the `withGraphQL` method:
|
|
280
364
|
|
|
281
365
|
```typescript
|
|
282
|
-
|
|
366
|
+
// Simple query
|
|
367
|
+
const query = "query { user { name email } }";
|
|
368
|
+
const data = await create
|
|
369
|
+
.post("https://api.example.com/graphql")
|
|
370
|
+
.withGraphQL(query)
|
|
371
|
+
.getJson();
|
|
372
|
+
|
|
373
|
+
// Query with variables
|
|
374
|
+
const queryWithVars = "query GetUser($id: ID!) { user(id: $id) { name email } }";
|
|
375
|
+
const user = await create
|
|
376
|
+
.post("https://api.example.com/graphql")
|
|
377
|
+
.withGraphQL(queryWithVars, { id: "123" })
|
|
378
|
+
.getJson();
|
|
379
|
+
|
|
380
|
+
// Mutation
|
|
381
|
+
const mutation =
|
|
382
|
+
"mutation CreateUser($name: String!) { createUser(name: $name) { id name } }";
|
|
383
|
+
const result = await create
|
|
384
|
+
.post("https://api.example.com/graphql")
|
|
385
|
+
.withGraphQL(mutation, { name: "John Doe" })
|
|
386
|
+
.getJson();
|
|
387
|
+
|
|
388
|
+
// Complex variables with nested objects
|
|
389
|
+
const complexQuery =
|
|
390
|
+
"query SearchUsers($filters: UserFilters!) { users(filters: $filters) { id name } }";
|
|
391
|
+
const results = await create
|
|
392
|
+
.post("https://api.example.com/graphql")
|
|
393
|
+
.withGraphQL(complexQuery, {
|
|
394
|
+
filters: {
|
|
395
|
+
name: "John",
|
|
396
|
+
age: 30,
|
|
397
|
+
tags: ["active", "verified"],
|
|
398
|
+
metadata: {
|
|
399
|
+
source: "web",
|
|
400
|
+
verified: true,
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
})
|
|
404
|
+
.getJson();
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Interceptors
|
|
408
|
+
|
|
409
|
+
Interceptors allow you to modify requests, transform responses, or handle errors globally or per-request. This is perfect for adding authentication tokens, logging, error recovery, and more.
|
|
410
|
+
|
|
411
|
+
#### Global Interceptors
|
|
412
|
+
|
|
413
|
+
Global interceptors apply to all requests:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// Add a global request interceptor (modify all requests)
|
|
417
|
+
const requestInterceptorId = create.config.addRequestInterceptor(config => {
|
|
418
|
+
// Add auth token to all requests
|
|
419
|
+
config.headers["Authorization"] = `Bearer ${getToken()}`;
|
|
420
|
+
// Modify URL, headers, body, etc.
|
|
421
|
+
return config;
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// Add a global response interceptor (transform all responses)
|
|
425
|
+
const responseInterceptorId = create.config.addResponseInterceptor(response => {
|
|
426
|
+
console.log(`Response received: ${response.status}`);
|
|
427
|
+
// Transform or modify the response
|
|
428
|
+
return response;
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Add a global error interceptor (handle all errors)
|
|
432
|
+
const errorInterceptorId = create.config.addErrorInterceptor(error => {
|
|
433
|
+
console.error("Request failed:", error.message);
|
|
434
|
+
// Can throw to propagate error, or return ResponseWrapper to recover
|
|
435
|
+
throw error;
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
// Remove interceptors when no longer needed
|
|
439
|
+
create.config.removeRequestInterceptor(requestInterceptorId);
|
|
440
|
+
create.config.removeResponseInterceptor(responseInterceptorId);
|
|
441
|
+
create.config.removeErrorInterceptor(errorInterceptorId);
|
|
442
|
+
|
|
443
|
+
// Clear all interceptors at once
|
|
444
|
+
create.config.clearInterceptors();
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### Per-Request Interceptors
|
|
448
|
+
|
|
449
|
+
Per-request interceptors apply only to a specific request:
|
|
283
450
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
451
|
+
```typescript
|
|
452
|
+
// Request interceptor - modify request configuration
|
|
453
|
+
const data = await create
|
|
454
|
+
.get("https://api.example.com/users")
|
|
455
|
+
.withRequestInterceptor(config => {
|
|
456
|
+
config.headers["X-Custom-Header"] = "value";
|
|
457
|
+
config.url = "https://api.example.com/modified-url"; // Can modify URL
|
|
458
|
+
return config;
|
|
289
459
|
})
|
|
290
|
-
.
|
|
291
|
-
|
|
292
|
-
//
|
|
293
|
-
const
|
|
294
|
-
.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
keyPrefix: 'user-data'
|
|
460
|
+
.getJson();
|
|
461
|
+
|
|
462
|
+
// Response interceptor - transform response
|
|
463
|
+
const transformed = await create
|
|
464
|
+
.get("https://api.example.com/users")
|
|
465
|
+
.withResponseInterceptor(response => {
|
|
466
|
+
console.log(`Got response with status ${response.status}`);
|
|
467
|
+
return response;
|
|
299
468
|
})
|
|
300
|
-
.
|
|
301
|
-
|
|
302
|
-
//
|
|
303
|
-
const
|
|
304
|
-
.
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
469
|
+
.getJson();
|
|
470
|
+
|
|
471
|
+
// Error interceptor - handle or recover from errors
|
|
472
|
+
const recovered = await create
|
|
473
|
+
.get("https://api.example.com/users")
|
|
474
|
+
.withErrorInterceptor(error => {
|
|
475
|
+
// Option 1: Throw to propagate error
|
|
476
|
+
throw error;
|
|
477
|
+
|
|
478
|
+
// Option 2: Return a ResponseWrapper to recover from error
|
|
479
|
+
// return new ResponseWrapper(fallbackResponse, error.url, error.method);
|
|
308
480
|
})
|
|
309
|
-
.
|
|
481
|
+
.getJson();
|
|
310
482
|
```
|
|
311
483
|
|
|
312
|
-
|
|
484
|
+
#### Interceptor Execution Order
|
|
485
|
+
|
|
486
|
+
Interceptors execute in a specific order:
|
|
313
487
|
|
|
314
|
-
|
|
488
|
+
1. **Request interceptors**: Global interceptors run first (in registration order), then per-request interceptors (in registration order)
|
|
489
|
+
2. **Response interceptors**: Per-request interceptors run first (in registration order), then global interceptors (in reverse registration order)
|
|
490
|
+
3. **Error interceptors**: Per-request interceptors run first (in registration order), then global interceptors (in reverse registration order)
|
|
315
491
|
|
|
316
492
|
```typescript
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
.
|
|
321
|
-
.
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
493
|
+
// Request: Global 1 → Global 2 → Per-request 1 → Per-request 2
|
|
494
|
+
// Response: Per-request 1 → Per-request 2 → Global 2 → Global 1
|
|
495
|
+
const data = await create
|
|
496
|
+
.get("https://api.example.com/users")
|
|
497
|
+
.withRequestInterceptor(() => console.log("Per-request 1"))
|
|
498
|
+
.withRequestInterceptor(() => console.log("Per-request 2"))
|
|
499
|
+
.getJson();
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
#### Advanced Interceptor Patterns
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
// Short-circuit request with early response
|
|
506
|
+
const cached = await create
|
|
507
|
+
.get("https://api.example.com/users")
|
|
508
|
+
.withRequestInterceptor(() => {
|
|
509
|
+
// Return early response from cache
|
|
510
|
+
return new Response(JSON.stringify(cachedData), {
|
|
511
|
+
status: 200,
|
|
512
|
+
headers: { "Content-Type": "application/json" },
|
|
513
|
+
});
|
|
514
|
+
})
|
|
515
|
+
.getJson();
|
|
516
|
+
|
|
517
|
+
// Recover from error with fallback
|
|
518
|
+
const fallback = await create
|
|
519
|
+
.get("https://api.example.com/users")
|
|
520
|
+
.withErrorInterceptor(error => {
|
|
521
|
+
// Return fallback response instead of throwing
|
|
522
|
+
const fallbackResponse = new Response(JSON.stringify({ users: [] }), {
|
|
523
|
+
status: 200,
|
|
524
|
+
headers: { "Content-Type": "application/json" },
|
|
525
|
+
});
|
|
526
|
+
return new ResponseWrapper(fallbackResponse, error.url, error.method);
|
|
527
|
+
})
|
|
528
|
+
.getJson();
|
|
529
|
+
|
|
530
|
+
// Async interceptors
|
|
531
|
+
const asyncData = await create
|
|
532
|
+
.get("https://api.example.com/users")
|
|
533
|
+
.withRequestInterceptor(async config => {
|
|
534
|
+
const token = await getTokenAsync();
|
|
535
|
+
config.headers["Authorization"] = `Bearer ${token}`;
|
|
536
|
+
return config;
|
|
537
|
+
})
|
|
538
|
+
.getJson();
|
|
326
539
|
```
|
|
327
540
|
|
|
328
541
|
### Request Cancellation
|
|
@@ -330,7 +543,8 @@ const products = await authRequest.sendTo('https://api.example.com/products').ge
|
|
|
330
543
|
```typescript
|
|
331
544
|
const controller = new AbortController();
|
|
332
545
|
|
|
333
|
-
const request = create
|
|
546
|
+
const request = create
|
|
547
|
+
.get("https://api.example.com/slow-endpoint")
|
|
334
548
|
.withTimeout(10000)
|
|
335
549
|
.withAbortController(controller);
|
|
336
550
|
|
|
@@ -338,170 +552,130 @@ const request = create.get()
|
|
|
338
552
|
setTimeout(() => controller.abort(), 2000);
|
|
339
553
|
|
|
340
554
|
try {
|
|
341
|
-
const data = await request.
|
|
555
|
+
const data = await request.getJson();
|
|
342
556
|
} catch (error) {
|
|
343
|
-
|
|
344
|
-
|
|
557
|
+
if (error.name === "AbortError") {
|
|
558
|
+
console.log("Request was cancelled by user");
|
|
559
|
+
} else if (error.isTimeout) {
|
|
560
|
+
console.log("Request timed out");
|
|
561
|
+
} else {
|
|
562
|
+
console.log("Other error:", error.message);
|
|
563
|
+
}
|
|
345
564
|
}
|
|
346
565
|
```
|
|
347
566
|
|
|
348
|
-
###
|
|
567
|
+
### URL Handling
|
|
349
568
|
|
|
350
|
-
|
|
351
|
-
interface User {
|
|
352
|
-
id: number;
|
|
353
|
-
name: string;
|
|
354
|
-
email: string;
|
|
355
|
-
}
|
|
569
|
+
The library handles both absolute and relative URLs, and automatically merges query parameters:
|
|
356
570
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
571
|
+
```typescript
|
|
572
|
+
// Relative URLs (preserved as-is)
|
|
573
|
+
const relative = await create.get("/api/users").getJson();
|
|
574
|
+
|
|
575
|
+
// Absolute URLs
|
|
576
|
+
const absolute = await create.get("https://api.example.com/users").getJson();
|
|
577
|
+
|
|
578
|
+
// Merging query params with existing URL params
|
|
579
|
+
const merged = await create
|
|
580
|
+
.get("https://api.example.com/users?page=1")
|
|
581
|
+
.withQueryParams({ limit: 20, sort: "name" })
|
|
582
|
+
.getJson();
|
|
583
|
+
// Result: https://api.example.com/users?page=1&limit=20&sort=name
|
|
584
|
+
|
|
585
|
+
// Special characters and unicode are properly encoded
|
|
586
|
+
const encoded = await create
|
|
587
|
+
.get("https://api.example.com/search")
|
|
588
|
+
.withQueryParams({ name: "用户名", filter: "status:active" })
|
|
589
|
+
.getJson();
|
|
590
|
+
```
|
|
371
591
|
|
|
372
|
-
|
|
373
|
-
const users = await userRequest
|
|
374
|
-
.sendTo('https://api.example.com/users')
|
|
375
|
-
.getData<ApiResponse, User[]>(data => data.users);
|
|
592
|
+
### Data Selection
|
|
376
593
|
|
|
377
|
-
|
|
378
|
-
const fullData = await userRequest
|
|
379
|
-
.sendTo('https://api.example.com/users')
|
|
380
|
-
.getData<ApiResponse>();
|
|
594
|
+
The `getData` method provides a powerful way to extract and transform specific data from API responses:
|
|
381
595
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
596
|
+
```typescript
|
|
597
|
+
// Extract specific properties from nested structures
|
|
598
|
+
const posts = await create
|
|
599
|
+
.get("https://api.example.com/feed")
|
|
600
|
+
.getData(data => data.feed.posts);
|
|
385
601
|
|
|
386
|
-
|
|
602
|
+
// Transform data in the selector function
|
|
603
|
+
const usernames = await create
|
|
604
|
+
.get("https://api.example.com/users")
|
|
605
|
+
.getData(data => data.users.map(user => user.username));
|
|
387
606
|
|
|
388
|
-
|
|
389
|
-
const
|
|
607
|
+
// Apply filtering in the selector
|
|
608
|
+
const activeUsers = await create
|
|
609
|
+
.get("https://api.example.com/users")
|
|
610
|
+
.getData(data => data.users.filter(user => user.isActive));
|
|
390
611
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
console.log('Data:', data);
|
|
398
|
-
})
|
|
399
|
-
.catch(error => {
|
|
400
|
-
console.error('Error:', error.message);
|
|
401
|
-
});
|
|
612
|
+
// Combine data from complex nested structures
|
|
613
|
+
const combinedData = await create.get("https://api.example.com/dashboard").getData(data => ({
|
|
614
|
+
userCount: data.stats.users.total,
|
|
615
|
+
recentPosts: data.content.recent.slice(0, 5),
|
|
616
|
+
notifications: data.user.notifications.unread,
|
|
617
|
+
}));
|
|
402
618
|
```
|
|
403
619
|
|
|
404
|
-
|
|
620
|
+
When a selector fails, the error message will contain helpful context to diagnose the issue:
|
|
405
621
|
|
|
406
622
|
```typescript
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
return localStorage.getItem(`my-app:${key}`);
|
|
417
|
-
},
|
|
418
|
-
set(key: string, value: string): void {
|
|
419
|
-
// Set in your custom storage
|
|
420
|
-
localStorage.setItem(`my-app:${key}`, value);
|
|
421
|
-
},
|
|
422
|
-
has(key: string): boolean {
|
|
423
|
-
// Check if exists in your custom storage
|
|
424
|
-
return localStorage.getItem(`my-app:${key}`) !== null;
|
|
425
|
-
},
|
|
426
|
-
delete(key: string): void {
|
|
427
|
-
// Remove from your custom storage
|
|
428
|
-
localStorage.removeItem(`my-app:${key}`);
|
|
429
|
-
},
|
|
430
|
-
clear(): void {
|
|
431
|
-
// Clear all items from your namespace
|
|
432
|
-
Object.keys(localStorage)
|
|
433
|
-
.filter(key => key.startsWith('my-app:'))
|
|
434
|
-
.forEach(key => localStorage.removeItem(key));
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
const request = create.get()
|
|
439
|
-
.withCache({ storage: customStorage })
|
|
440
|
-
.sendTo('https://api.example.com/data');
|
|
623
|
+
try {
|
|
624
|
+
// This will fail if the response structure doesn't match expectations
|
|
625
|
+
const result = await create
|
|
626
|
+
.get("https://api.example.com/data")
|
|
627
|
+
.getData(data => data.results.items);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
console.error(error);
|
|
630
|
+
// Error message includes context for debugging
|
|
631
|
+
}
|
|
441
632
|
```
|
|
442
633
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
You can create your own storage provider by implementing the `StorageProvider` interface:
|
|
634
|
+
## TypeScript Support
|
|
446
635
|
|
|
447
636
|
```typescript
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
return localStorage.getItem(`${namespace}:${key}`);
|
|
455
|
-
},
|
|
456
|
-
|
|
457
|
-
set(key: string, value: string): void {
|
|
458
|
-
try {
|
|
459
|
-
localStorage.setItem(`${namespace}:${key}`, value);
|
|
460
|
-
} catch (e) {
|
|
461
|
-
// Handle quota exceeded errors
|
|
462
|
-
console.warn("Storage quota exceeded, clearing cache");
|
|
463
|
-
this.clear();
|
|
464
|
-
try {
|
|
465
|
-
localStorage.setItem(`${namespace}:${key}`, value);
|
|
466
|
-
} catch (e) {
|
|
467
|
-
console.error("Failed to set cache item even after clearing cache");
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
},
|
|
471
|
-
|
|
472
|
-
has(key: string): boolean {
|
|
473
|
-
return localStorage.getItem(`${namespace}:${key}`) !== null;
|
|
474
|
-
},
|
|
475
|
-
|
|
476
|
-
delete(key: string): void {
|
|
477
|
-
localStorage.removeItem(`${namespace}:${key}`);
|
|
478
|
-
},
|
|
637
|
+
interface User {
|
|
638
|
+
id: number;
|
|
639
|
+
name: string;
|
|
640
|
+
email: string;
|
|
641
|
+
isActive: boolean;
|
|
642
|
+
}
|
|
479
643
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
644
|
+
interface ApiResponse<T> {
|
|
645
|
+
data: T;
|
|
646
|
+
meta: {
|
|
647
|
+
total: number;
|
|
648
|
+
page: number;
|
|
486
649
|
};
|
|
487
650
|
}
|
|
488
651
|
|
|
489
|
-
//
|
|
490
|
-
const
|
|
491
|
-
.
|
|
492
|
-
|
|
493
|
-
ttl: 24 * 60 * 60 * 1000 // 24 hours
|
|
494
|
-
})
|
|
495
|
-
.sendTo('https://api.example.com/data');
|
|
496
|
-
```
|
|
652
|
+
// Type the full response
|
|
653
|
+
const response = await create
|
|
654
|
+
.get("https://api.example.com/users")
|
|
655
|
+
.getJson<ApiResponse<User[]>>();
|
|
497
656
|
|
|
498
|
-
|
|
657
|
+
// Or use getData with type parameters
|
|
658
|
+
const users = await create
|
|
659
|
+
.get("https://api.example.com/users")
|
|
660
|
+
.getData<ApiResponse<User[]>, User[]>(data => data.data);
|
|
499
661
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
662
|
+
// TypeScript knows the types
|
|
663
|
+
users.forEach(user => {
|
|
664
|
+
console.log(`${user.name} (${user.email}): ${user.isActive ? "Active" : "Inactive"}`);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
// Function with proper types
|
|
668
|
+
async function getUserById(id: number): Promise<User> {
|
|
669
|
+
return create
|
|
670
|
+
.get("https://api.example.com/users")
|
|
671
|
+
.withQueryParam("id", id)
|
|
672
|
+
.getData<ApiResponse<User[]>, User>(data => {
|
|
673
|
+
const user = data.data[0];
|
|
674
|
+
if (!user) throw new Error(`User with ID ${id} not found`);
|
|
675
|
+
return user;
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
```
|
|
505
679
|
|
|
506
680
|
## CSRF Protection
|
|
507
681
|
|
|
@@ -523,11 +697,15 @@ You can configure CSRF settings globally for all requests:
|
|
|
523
697
|
|
|
524
698
|
```typescript
|
|
525
699
|
// Configure CSRF settings for all requests
|
|
526
|
-
create.config.setCsrfToken(
|
|
527
|
-
create.config.setCsrfHeaderName(
|
|
528
|
-
create.config.setXsrfCookieName(
|
|
529
|
-
create.config.
|
|
530
|
-
create.config.
|
|
700
|
+
create.config.setCsrfToken("your-csrf-token");
|
|
701
|
+
create.config.setCsrfHeaderName("X-CSRF-Token"); // Default header name for CSRF token
|
|
702
|
+
create.config.setXsrfCookieName("XSRF-TOKEN"); // Default cookie name to read from
|
|
703
|
+
create.config.setXsrfHeaderName("X-XSRF-TOKEN"); // Default header name for XSRF token from cookie
|
|
704
|
+
create.config.setEnableAntiCsrf(true); // Enable/disable X-Requested-With header
|
|
705
|
+
create.config.setEnableAutoXsrf(true); // Enable/disable automatic cookie-to-header token
|
|
706
|
+
|
|
707
|
+
// Reset all configuration to defaults
|
|
708
|
+
create.config.reset();
|
|
531
709
|
```
|
|
532
710
|
|
|
533
711
|
### Per-Request CSRF Settings
|
|
@@ -536,22 +714,22 @@ You can also configure CSRF protection on individual requests:
|
|
|
536
714
|
|
|
537
715
|
```typescript
|
|
538
716
|
// Configure CSRF for a specific request
|
|
539
|
-
const request = create
|
|
540
|
-
.
|
|
717
|
+
const request = create
|
|
718
|
+
.post("https://api.example.com/users")
|
|
719
|
+
.withCsrfToken("request-specific-token") // Set a specific token
|
|
541
720
|
.withAntiCsrfHeaders() // Explicitly add X-Requested-With header
|
|
542
721
|
.withoutCsrfProtection(); // Or disable all automatic CSRF protection
|
|
543
722
|
```
|
|
544
723
|
|
|
545
|
-
|
|
724
|
+
## Performance Considerations
|
|
546
725
|
|
|
547
|
-
|
|
726
|
+
create-request is designed to be lightweight and efficient:
|
|
548
727
|
|
|
549
|
-
- **
|
|
550
|
-
- **
|
|
551
|
-
- **
|
|
552
|
-
- **
|
|
553
|
-
|
|
554
|
-
When your server sends a CSRF token in a cookie or response header, `create-request` can automatically extract and include it in subsequent requests.
|
|
728
|
+
- **Zero Dependencies**: No extra libraries to load
|
|
729
|
+
- **Tree-Shakable**: Only import what you need
|
|
730
|
+
- **Minimal Overhead**: Thin wrapper around the native Fetch API
|
|
731
|
+
- **Memory Efficient**: Doesn't create unnecessary objects
|
|
732
|
+
- **Clean API**: Simple and intuitive interface
|
|
555
733
|
|
|
556
734
|
## Browser Support
|
|
557
735
|
|
|
@@ -563,6 +741,35 @@ This library works with all browsers that support the Fetch API:
|
|
|
563
741
|
- Edge 14+
|
|
564
742
|
- Opera 29+
|
|
565
743
|
|
|
744
|
+
## Comparison of JavaScript HTTP Client Libraries
|
|
745
|
+
|
|
746
|
+
| Feature | create-request | Fetch | Axios | SuperAgent | Got | Ky | node-fetch | Redaxios |
|
|
747
|
+
| ------------------- | -------------- | ------ | ------- | ---------- | ------- | ------ | ---------- | -------- |
|
|
748
|
+
| **Size (min+gzip)** | ~5.5KB | Native | ~13.6KB | ~17.8KB | ~17.8KB | ~3.4KB | ~7.7KB | ~1KB |
|
|
749
|
+
| **Browser** | Modern | Modern | IE11+ | IE9+ | ❌ No | Modern | ❌ No | Modern |
|
|
750
|
+
| **Node.js** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
751
|
+
| **HTTP/2** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
752
|
+
| **Auto Retries** | ✅ | ❌ | 🛠️ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
753
|
+
| **Cancellation** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
754
|
+
| **Auto JSON** | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
755
|
+
| **Timeout** | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
756
|
+
| **TypeScript** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
757
|
+
| **Streaming** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
758
|
+
| **Progress** | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
759
|
+
| **Middleware** | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
760
|
+
| **Cookies** | ✅ | ✅ | 🛠️ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
761
|
+
| **Pagination API** | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
762
|
+
| **Zero Deps** | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
|
|
763
|
+
| **Chainable API** | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
764
|
+
| **CSRF Protection** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
765
|
+
| **GraphQL Support** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
766
|
+
| **Interceptors** | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
767
|
+
|
|
768
|
+
**Notes:**
|
|
769
|
+
|
|
770
|
+
- "Modern" browser support: Chrome 42+, Firefox 39+, Safari 10.1+, Edge 14+, Opera 29+
|
|
771
|
+
- 🛠️ Feature requires additional plugins or adapters (not available out-of-the-box)
|
|
772
|
+
|
|
566
773
|
## License
|
|
567
774
|
|
|
568
775
|
MIT
|