create-request 0.0.1-beta.9 → 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 +534 -303
- package/dist/library/BaseRequest.d.ts +274 -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 +1321 -725
- 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 -1525
- 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,13 +122,20 @@ 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
|
|
@@ -114,138 +147,189 @@ const request = create.get()
|
|
|
114
147
|
})
|
|
115
148
|
|
|
116
149
|
// Authentication methods
|
|
117
|
-
.withBearerToken(
|
|
118
|
-
.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
|
+
})
|
|
119
163
|
|
|
120
|
-
// URL parameters
|
|
121
|
-
.withQueryParams({ search:
|
|
122
|
-
.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
|
|
123
168
|
|
|
124
169
|
// Request body configuration (for POST/PUT/PATCH)
|
|
125
|
-
.withContentType(
|
|
170
|
+
.withContentType("application/json") // Set specific content type
|
|
126
171
|
|
|
127
172
|
// Fetch API options
|
|
128
|
-
|
|
129
|
-
.
|
|
130
|
-
.
|
|
131
|
-
.
|
|
132
|
-
.
|
|
133
|
-
.
|
|
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
|
|
134
181
|
```
|
|
135
182
|
|
|
136
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:
|
|
137
184
|
|
|
138
185
|
```typescript
|
|
139
186
|
// Simple example with just what's needed
|
|
140
|
-
const
|
|
187
|
+
const users = await create
|
|
188
|
+
.get("https://api.example.com/users")
|
|
141
189
|
.withBearerToken(userToken)
|
|
142
190
|
.withQueryParams({ q: searchTerm, limit: 20 })
|
|
143
|
-
.withTimeout(3000)
|
|
144
|
-
|
|
145
|
-
// Now execute the request
|
|
146
|
-
const users = await searchUsers.sendTo('https://api.example.com/users').getData();
|
|
191
|
+
.withTimeout(3000)
|
|
192
|
+
.getData();
|
|
147
193
|
```
|
|
148
194
|
|
|
149
|
-
|
|
195
|
+
### Request Bodies (POST/PUT/PATCH)
|
|
150
196
|
|
|
151
197
|
```typescript
|
|
152
|
-
//
|
|
153
|
-
const
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
'Accept-Language': 'en-US'
|
|
157
|
-
})
|
|
158
|
-
.withBearerToken(authToken)
|
|
159
|
-
.withTimeout(5000)
|
|
160
|
-
.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 });
|
|
161
202
|
|
|
162
|
-
//
|
|
163
|
-
const
|
|
164
|
-
|
|
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);
|
|
165
221
|
```
|
|
166
222
|
|
|
167
|
-
###
|
|
223
|
+
### GraphQL Requests
|
|
224
|
+
|
|
225
|
+
The library provides built-in support for GraphQL queries and mutations:
|
|
168
226
|
|
|
169
227
|
```typescript
|
|
170
|
-
//
|
|
171
|
-
const
|
|
172
|
-
.
|
|
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
|
+
```
|
|
173
249
|
|
|
174
|
-
|
|
175
|
-
const textRequest = create.post()
|
|
176
|
-
.withBody('Plain text content');
|
|
250
|
+
The `withGraphQL` method automatically:
|
|
177
251
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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:
|
|
182
261
|
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
185
289
|
```
|
|
186
290
|
|
|
187
291
|
### Executing Requests
|
|
188
292
|
|
|
189
293
|
```typescript
|
|
190
|
-
//
|
|
191
|
-
const response = await
|
|
294
|
+
// Get the full response
|
|
295
|
+
const response = await create.get("https://api.example.com/endpoint").getResponse();
|
|
192
296
|
|
|
193
297
|
// With direct data extraction
|
|
194
|
-
const jsonData = await
|
|
195
|
-
const textData = await
|
|
196
|
-
const blobData = await
|
|
197
|
-
const bodyStream = await
|
|
198
|
-
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();
|
|
199
302
|
|
|
200
303
|
// Using the data selector API to extract specific data
|
|
201
|
-
const userData = await
|
|
304
|
+
const userData = await create
|
|
305
|
+
.get("https://api.example.com/users")
|
|
202
306
|
.getData(data => data.results.users);
|
|
203
307
|
|
|
204
308
|
// Using the data selector without a selector function just returns the full JSON response
|
|
205
|
-
const fullData = await
|
|
309
|
+
const fullData = await create.get("https://api.example.com/data").getData();
|
|
206
310
|
```
|
|
207
311
|
|
|
208
|
-
###
|
|
312
|
+
### ResponseWrapper Properties
|
|
209
313
|
|
|
210
|
-
|
|
314
|
+
When you use `getResponse()`, you get a `ResponseWrapper` object that provides convenient access to response properties and methods:
|
|
211
315
|
|
|
212
316
|
```typescript
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const combinedData = await request
|
|
230
|
-
.sendTo('https://api.example.com/dashboard')
|
|
231
|
-
.getData(data => ({
|
|
232
|
-
userCount: data.stats.users.total,
|
|
233
|
-
recentPosts: data.content.recent.slice(0, 5),
|
|
234
|
-
notifications: data.user.notifications.unread
|
|
235
|
-
}));
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
When a selector fails, the error message will contain the original response data to help diagnose the issue:
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
try {
|
|
242
|
-
const result = await request
|
|
243
|
-
.sendTo('https://api.example.com/data')
|
|
244
|
-
.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
|
-
}
|
|
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
|
|
249
333
|
```
|
|
250
334
|
|
|
251
335
|
### Error Handling
|
|
@@ -254,14 +338,15 @@ All errors from requests are instances of `RequestError` with detailed informati
|
|
|
254
338
|
|
|
255
339
|
```typescript
|
|
256
340
|
try {
|
|
257
|
-
const data = await
|
|
341
|
+
const data = await create.get("https://api.example.com/data").getJson();
|
|
258
342
|
} catch (error) {
|
|
259
343
|
// error will always be a RequestError
|
|
260
|
-
console.log(error.message);
|
|
261
|
-
console.log(error.status);
|
|
262
|
-
console.log(error.url);
|
|
263
|
-
console.log(error.method);
|
|
264
|
-
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
|
|
265
350
|
|
|
266
351
|
// Access the original response if available
|
|
267
352
|
if (error.response) {
|
|
@@ -271,53 +356,186 @@ try {
|
|
|
271
356
|
}
|
|
272
357
|
```
|
|
273
358
|
|
|
274
|
-
|
|
359
|
+
## Advanced Usage
|
|
360
|
+
|
|
361
|
+
### GraphQL Support
|
|
362
|
+
|
|
363
|
+
`create-request` provides first-class support for GraphQL with the `withGraphQL` method:
|
|
275
364
|
|
|
276
365
|
```typescript
|
|
277
|
-
|
|
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:
|
|
278
450
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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;
|
|
284
459
|
})
|
|
285
|
-
.
|
|
286
|
-
|
|
287
|
-
//
|
|
288
|
-
const
|
|
289
|
-
.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
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;
|
|
294
468
|
})
|
|
295
|
-
.
|
|
296
|
-
|
|
297
|
-
//
|
|
298
|
-
const
|
|
299
|
-
.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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);
|
|
303
480
|
})
|
|
304
|
-
.
|
|
481
|
+
.getJson();
|
|
305
482
|
```
|
|
306
483
|
|
|
307
|
-
|
|
484
|
+
#### Interceptor Execution Order
|
|
485
|
+
|
|
486
|
+
Interceptors execute in a specific order:
|
|
308
487
|
|
|
309
|
-
|
|
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)
|
|
310
491
|
|
|
311
492
|
```typescript
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
.
|
|
316
|
-
.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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();
|
|
321
539
|
```
|
|
322
540
|
|
|
323
541
|
### Request Cancellation
|
|
@@ -325,7 +543,8 @@ const products = await authRequest.sendTo('https://api.example.com/products').ge
|
|
|
325
543
|
```typescript
|
|
326
544
|
const controller = new AbortController();
|
|
327
545
|
|
|
328
|
-
const request = create
|
|
546
|
+
const request = create
|
|
547
|
+
.get("https://api.example.com/slow-endpoint")
|
|
329
548
|
.withTimeout(10000)
|
|
330
549
|
.withAbortController(controller);
|
|
331
550
|
|
|
@@ -333,151 +552,130 @@ const request = create.get()
|
|
|
333
552
|
setTimeout(() => controller.abort(), 2000);
|
|
334
553
|
|
|
335
554
|
try {
|
|
336
|
-
const data = await request.
|
|
555
|
+
const data = await request.getJson();
|
|
337
556
|
} catch (error) {
|
|
338
|
-
|
|
339
|
-
|
|
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
|
+
}
|
|
340
564
|
}
|
|
341
565
|
```
|
|
342
566
|
|
|
343
|
-
###
|
|
344
|
-
|
|
345
|
-
```typescript
|
|
346
|
-
interface User {
|
|
347
|
-
id: number;
|
|
348
|
-
name: string;
|
|
349
|
-
email: string;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
interface ApiResponse {
|
|
353
|
-
users: User[];
|
|
354
|
-
pagination: {
|
|
355
|
-
total: number;
|
|
356
|
-
page: number;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
567
|
+
### URL Handling
|
|
359
568
|
|
|
360
|
-
|
|
569
|
+
The library handles both absolute and relative URLs, and automatically merges query parameters:
|
|
361
570
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
+
```
|
|
366
591
|
|
|
367
|
-
|
|
368
|
-
const users = await userRequest
|
|
369
|
-
.sendTo('https://api.example.com/users')
|
|
370
|
-
.getData<ApiResponse, User[]>(data => data.users);
|
|
592
|
+
### Data Selection
|
|
371
593
|
|
|
372
|
-
|
|
373
|
-
const fullData = await userRequest
|
|
374
|
-
.sendTo('https://api.example.com/users')
|
|
375
|
-
.getData<ApiResponse>();
|
|
594
|
+
The `getData` method provides a powerful way to extract and transform specific data from API responses:
|
|
376
595
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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);
|
|
380
601
|
|
|
381
|
-
|
|
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));
|
|
382
606
|
|
|
383
|
-
|
|
384
|
-
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));
|
|
385
611
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
console.log('Data:', data);
|
|
393
|
-
})
|
|
394
|
-
.catch(error => {
|
|
395
|
-
console.error('Error:', error.message);
|
|
396
|
-
});
|
|
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
|
+
}));
|
|
397
618
|
```
|
|
398
619
|
|
|
399
|
-
|
|
620
|
+
When a selector fails, the error message will contain helpful context to diagnose the issue:
|
|
400
621
|
|
|
401
622
|
```typescript
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const userMatch = authHeader.match(/User-(\d+)/);
|
|
412
|
-
const userId = userMatch ? userMatch[1] : 'anonymous';
|
|
413
|
-
return `${method}:${userId}:${url}`;
|
|
414
|
-
}
|
|
415
|
-
})
|
|
416
|
-
.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
|
+
}
|
|
417
632
|
```
|
|
418
633
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
You can create your own storage provider by implementing the `StorageProvider` interface:
|
|
634
|
+
## TypeScript Support
|
|
422
635
|
|
|
423
636
|
```typescript
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
return localStorage.getItem(`${namespace}:${key}`);
|
|
431
|
-
},
|
|
432
|
-
|
|
433
|
-
set(key: string, value: string): void {
|
|
434
|
-
try {
|
|
435
|
-
localStorage.setItem(`${namespace}:${key}`, value);
|
|
436
|
-
} catch (e) {
|
|
437
|
-
// Handle quota exceeded errors
|
|
438
|
-
console.warn("Storage quota exceeded, clearing cache");
|
|
439
|
-
this.clear();
|
|
440
|
-
try {
|
|
441
|
-
localStorage.setItem(`${namespace}:${key}`, value);
|
|
442
|
-
} catch (e) {
|
|
443
|
-
console.error("Failed to set cache item even after clearing cache");
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
},
|
|
447
|
-
|
|
448
|
-
has(key: string): boolean {
|
|
449
|
-
return localStorage.getItem(`${namespace}:${key}`) !== null;
|
|
450
|
-
},
|
|
451
|
-
|
|
452
|
-
delete(key: string): void {
|
|
453
|
-
localStorage.removeItem(`${namespace}:${key}`);
|
|
454
|
-
},
|
|
637
|
+
interface User {
|
|
638
|
+
id: number;
|
|
639
|
+
name: string;
|
|
640
|
+
email: string;
|
|
641
|
+
isActive: boolean;
|
|
642
|
+
}
|
|
455
643
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
644
|
+
interface ApiResponse<T> {
|
|
645
|
+
data: T;
|
|
646
|
+
meta: {
|
|
647
|
+
total: number;
|
|
648
|
+
page: number;
|
|
462
649
|
};
|
|
463
650
|
}
|
|
464
651
|
|
|
465
|
-
//
|
|
466
|
-
const
|
|
467
|
-
.
|
|
468
|
-
|
|
469
|
-
ttl: 24 * 60 * 60 * 1000 // 24 hours
|
|
470
|
-
})
|
|
471
|
-
.sendTo('https://api.example.com/data');
|
|
472
|
-
```
|
|
652
|
+
// Type the full response
|
|
653
|
+
const response = await create
|
|
654
|
+
.get("https://api.example.com/users")
|
|
655
|
+
.getJson<ApiResponse<User[]>>();
|
|
473
656
|
|
|
474
|
-
|
|
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);
|
|
475
661
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
+
```
|
|
481
679
|
|
|
482
680
|
## CSRF Protection
|
|
483
681
|
|
|
@@ -499,11 +697,15 @@ You can configure CSRF settings globally for all requests:
|
|
|
499
697
|
|
|
500
698
|
```typescript
|
|
501
699
|
// Configure CSRF settings for all requests
|
|
502
|
-
create.config.setCsrfToken(
|
|
503
|
-
create.config.setCsrfHeaderName(
|
|
504
|
-
create.config.setXsrfCookieName(
|
|
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
|
|
505
704
|
create.config.setEnableAntiCsrf(true); // Enable/disable X-Requested-With header
|
|
506
705
|
create.config.setEnableAutoXsrf(true); // Enable/disable automatic cookie-to-header token
|
|
706
|
+
|
|
707
|
+
// Reset all configuration to defaults
|
|
708
|
+
create.config.reset();
|
|
507
709
|
```
|
|
508
710
|
|
|
509
711
|
### Per-Request CSRF Settings
|
|
@@ -512,22 +714,22 @@ You can also configure CSRF protection on individual requests:
|
|
|
512
714
|
|
|
513
715
|
```typescript
|
|
514
716
|
// Configure CSRF for a specific request
|
|
515
|
-
const request = create
|
|
516
|
-
.
|
|
717
|
+
const request = create
|
|
718
|
+
.post("https://api.example.com/users")
|
|
719
|
+
.withCsrfToken("request-specific-token") // Set a specific token
|
|
517
720
|
.withAntiCsrfHeaders() // Explicitly add X-Requested-With header
|
|
518
721
|
.withoutCsrfProtection(); // Or disable all automatic CSRF protection
|
|
519
722
|
```
|
|
520
723
|
|
|
521
|
-
|
|
724
|
+
## Performance Considerations
|
|
522
725
|
|
|
523
|
-
|
|
726
|
+
create-request is designed to be lightweight and efficient:
|
|
524
727
|
|
|
525
|
-
- **
|
|
526
|
-
- **
|
|
527
|
-
- **
|
|
528
|
-
- **
|
|
529
|
-
|
|
530
|
-
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
|
|
531
733
|
|
|
532
734
|
## Browser Support
|
|
533
735
|
|
|
@@ -539,6 +741,35 @@ This library works with all browsers that support the Fetch API:
|
|
|
539
741
|
- Edge 14+
|
|
540
742
|
- Opera 29+
|
|
541
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
|
+
|
|
542
773
|
## License
|
|
543
774
|
|
|
544
775
|
MIT
|