create-request 0.0.1-beta.9 → 1.1.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 +508 -303
- package/dist/library/BaseRequest.d.ts +301 -41
- package/dist/library/BodyRequest.d.ts +39 -4
- package/dist/library/RequestError.d.ts +5 -2
- package/dist/library/ResponseWrapper.d.ts +96 -13
- package/dist/library/enums.d.ts +0 -11
- package/dist/library/index.cjs +2201 -0
- package/dist/library/index.cjs.map +1 -0
- package/dist/library/index.d.ts +12 -12
- package/dist/library/index.esm.js +1416 -728
- 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 +47 -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
95
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
```bash
|
|
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,209 @@ 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();
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
You can also create reusable base requests with common settings:
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// Create base authenticated request
|
|
153
|
-
const apiBase = create.get()
|
|
154
|
-
.withHeaders({
|
|
155
|
-
'X-API-Version': '1.2',
|
|
156
|
-
'Accept-Language': 'en-US'
|
|
157
|
-
})
|
|
158
|
-
.withBearerToken(authToken)
|
|
159
|
-
.withTimeout(5000)
|
|
160
|
-
.withRetries(2);
|
|
161
|
-
|
|
162
|
-
// Use the base request for different endpoints
|
|
163
|
-
const users = await apiBase.sendTo('https://api.example.com/users').getData();
|
|
164
|
-
const products = await apiBase.sendTo('https://api.example.com/products').getData();
|
|
191
|
+
.withTimeout(3000)
|
|
192
|
+
.getData();
|
|
165
193
|
```
|
|
166
194
|
|
|
167
195
|
### Request Bodies (POST/PUT/PATCH)
|
|
168
196
|
|
|
169
197
|
```typescript
|
|
170
|
-
// JSON body
|
|
171
|
-
const jsonRequest = create
|
|
172
|
-
.
|
|
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 });
|
|
173
202
|
|
|
174
|
-
// String body
|
|
175
|
-
const textRequest = create
|
|
176
|
-
.
|
|
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");
|
|
177
207
|
|
|
178
208
|
// Form data
|
|
179
209
|
const formData = new FormData();
|
|
180
|
-
formData.append(
|
|
181
|
-
formData.append(
|
|
210
|
+
formData.append("name", "John");
|
|
211
|
+
formData.append("file", fileBlob);
|
|
182
212
|
|
|
183
|
-
const formRequest = create.post()
|
|
184
|
-
|
|
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);
|
|
185
221
|
```
|
|
186
222
|
|
|
187
|
-
###
|
|
223
|
+
### GraphQL Requests
|
|
224
|
+
|
|
225
|
+
The library provides built-in support for GraphQL queries and mutations:
|
|
188
226
|
|
|
189
227
|
```typescript
|
|
190
|
-
//
|
|
191
|
-
const
|
|
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
|
+
```
|
|
192
249
|
|
|
193
|
-
|
|
194
|
-
const jsonData = await request.sendTo('https://api.example.com/endpoint').getJson();
|
|
195
|
-
const textData = await request.sendTo('https://api.example.com/endpoint').getText();
|
|
196
|
-
const blobData = await request.sendTo('https://api.example.com/endpoint').getBlob();
|
|
197
|
-
const bodyStream = await request.sendTo('https://api.example.com/endpoint').getBody();
|
|
198
|
-
const arrayBuffer = await request.sendTo('https://api.example.com/endpoint').getArrayBuffer();
|
|
250
|
+
#### GraphQL Error Handling
|
|
199
251
|
|
|
200
|
-
|
|
201
|
-
const userData = await request.sendTo('https://api.example.com/users')
|
|
202
|
-
.getData(data => data.results.users);
|
|
252
|
+
GraphQL errors do not cause exceptions by default. Use the `throwOnError` option to make them throw exceptions:
|
|
203
253
|
|
|
204
|
-
|
|
205
|
-
|
|
254
|
+
```typescript
|
|
255
|
+
// Throw an error if the GraphQL response contains errors
|
|
256
|
+
try {
|
|
257
|
+
const user = await create
|
|
258
|
+
.post("https://api.example.com/graphql")
|
|
259
|
+
.withGraphQL(
|
|
260
|
+
"query GetUser($id: ID!) { user(id: $id) { name email } }",
|
|
261
|
+
{ id: "123" },
|
|
262
|
+
{ throwOnError: true }
|
|
263
|
+
)
|
|
264
|
+
.getJson();
|
|
265
|
+
} catch (error) {
|
|
266
|
+
console.error(error.message);
|
|
267
|
+
}
|
|
206
268
|
```
|
|
207
269
|
|
|
208
|
-
|
|
270
|
+
The `withGraphQL` method automatically:
|
|
209
271
|
|
|
210
|
-
|
|
272
|
+
- Formats the body as JSON with `query` and optional `variables` properties
|
|
273
|
+
- Sets `Content-Type` to `application/json`
|
|
274
|
+
- Validates the query is non-empty
|
|
275
|
+
- Validates variables are a plain object (not arrays or null)
|
|
276
|
+
- Optionally throws errors when GraphQL response contains errors (with `throwOnError: true`)
|
|
277
|
+
|
|
278
|
+
### Query Parameters Advanced Features
|
|
279
|
+
|
|
280
|
+
The library supports advanced query parameter handling:
|
|
211
281
|
|
|
212
282
|
```typescript
|
|
213
|
-
//
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
283
|
+
// Array values create multiple query params with the same key
|
|
284
|
+
const request = create.get("https://api.example.com/search").withQueryParams({
|
|
285
|
+
tags: ["javascript", "typescript", "node"], // ?tags=javascript&tags=typescript&tags=node
|
|
286
|
+
page: 1,
|
|
287
|
+
active: true,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Null and undefined values are automatically filtered out
|
|
291
|
+
const filtered = create.get("https://api.example.com/users").withQueryParams({
|
|
292
|
+
name: "John",
|
|
293
|
+
age: null, // Ignored
|
|
294
|
+
email: undefined, // Ignored
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Supports all JavaScript types (strings, numbers, booleans, arrays)
|
|
298
|
+
const typed = create.get("https://api.example.com/data").withQueryParams({
|
|
299
|
+
page: 1, // Number
|
|
300
|
+
active: true, // Boolean
|
|
301
|
+
tags: ["js", "ts"], // Array
|
|
302
|
+
name: "John", // String
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Merge with existing query params in URL
|
|
306
|
+
const merged = create
|
|
307
|
+
.get("https://api.example.com/users?existing=value")
|
|
308
|
+
.withQueryParams({ new: "param" }); // Both existing and new params included
|
|
309
|
+
```
|
|
217
310
|
|
|
218
|
-
|
|
219
|
-
const usernames = await request
|
|
220
|
-
.sendTo('https://api.example.com/users')
|
|
221
|
-
.getData(data => data.users.map(user => user.username));
|
|
311
|
+
### Executing Requests
|
|
222
312
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
.getData(data => data.users.filter(user => user.isActive));
|
|
313
|
+
```typescript
|
|
314
|
+
// Get the full response
|
|
315
|
+
const response = await create.get("https://api.example.com/endpoint").getResponse();
|
|
227
316
|
|
|
228
|
-
//
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
317
|
+
// With direct data extraction
|
|
318
|
+
const jsonData = await create.get("https://api.example.com/endpoint").getJson();
|
|
319
|
+
const textData = await create.get("https://api.example.com/endpoint").getText();
|
|
320
|
+
const blobData = await create.get("https://api.example.com/endpoint").getBlob();
|
|
321
|
+
const bodyStream = await create.get("https://api.example.com/endpoint").getBody();
|
|
322
|
+
|
|
323
|
+
// Using the data selector API to extract specific data
|
|
324
|
+
const userData = await create
|
|
325
|
+
.get("https://api.example.com/users")
|
|
326
|
+
.getData(data => data.results.users);
|
|
327
|
+
|
|
328
|
+
// Using the data selector without a selector function just returns the full JSON response
|
|
329
|
+
const fullData = await create.get("https://api.example.com/data").getData();
|
|
236
330
|
```
|
|
237
331
|
|
|
238
|
-
|
|
332
|
+
### ResponseWrapper Properties
|
|
333
|
+
|
|
334
|
+
When you use `getResponse()`, you get a `ResponseWrapper` object that provides convenient access to response properties and methods:
|
|
239
335
|
|
|
240
336
|
```typescript
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
337
|
+
const response = await create.get("https://api.example.com/users").getResponse();
|
|
338
|
+
|
|
339
|
+
// Access response properties directly
|
|
340
|
+
console.log(response.status); // HTTP status code (e.g., 200)
|
|
341
|
+
console.log(response.statusText); // Status text (e.g., "OK")
|
|
342
|
+
console.log(response.ok); // Boolean: true if status is 200-299
|
|
343
|
+
console.log(response.headers); // Headers object
|
|
344
|
+
console.log(response.url); // Request URL
|
|
345
|
+
console.log(response.method); // HTTP method
|
|
346
|
+
console.log(response.raw); // Raw Response object from fetch
|
|
347
|
+
|
|
348
|
+
// Use wrapper methods for body parsing
|
|
349
|
+
const json = await response.getJson();
|
|
350
|
+
const text = await response.getText();
|
|
351
|
+
const blob = await response.getBlob();
|
|
352
|
+
const stream = response.getBody(); // ReadableStream or null
|
|
249
353
|
```
|
|
250
354
|
|
|
251
355
|
### Error Handling
|
|
@@ -254,14 +358,15 @@ All errors from requests are instances of `RequestError` with detailed informati
|
|
|
254
358
|
|
|
255
359
|
```typescript
|
|
256
360
|
try {
|
|
257
|
-
const data = await
|
|
361
|
+
const data = await create.get("https://api.example.com/data").getJson();
|
|
258
362
|
} catch (error) {
|
|
259
363
|
// 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.
|
|
364
|
+
console.log(error.message); // Error message
|
|
365
|
+
console.log(error.status); // HTTP status code (if available)
|
|
366
|
+
console.log(error.url); // Request URL
|
|
367
|
+
console.log(error.method); // HTTP method
|
|
368
|
+
console.log(error.isTimeout); // Whether it was a timeout
|
|
369
|
+
console.log(error.isAborted); // Whether it was aborted/cancelled
|
|
265
370
|
|
|
266
371
|
// Access the original response if available
|
|
267
372
|
if (error.response) {
|
|
@@ -271,53 +376,140 @@ try {
|
|
|
271
376
|
}
|
|
272
377
|
```
|
|
273
378
|
|
|
274
|
-
|
|
379
|
+
## Advanced Usage
|
|
380
|
+
|
|
381
|
+
### Interceptors
|
|
382
|
+
|
|
383
|
+
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.
|
|
384
|
+
|
|
385
|
+
#### Global Interceptors
|
|
386
|
+
|
|
387
|
+
Global interceptors apply to all requests:
|
|
275
388
|
|
|
276
389
|
```typescript
|
|
277
|
-
|
|
390
|
+
// Add a global request interceptor (modify all requests)
|
|
391
|
+
const requestInterceptorId = create.config.addRequestInterceptor(config => {
|
|
392
|
+
// Add auth token to all requests
|
|
393
|
+
config.headers["Authorization"] = `Bearer ${getToken()}`;
|
|
394
|
+
// Modify URL, headers, body, etc.
|
|
395
|
+
return config;
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Add a global response interceptor (transform all responses)
|
|
399
|
+
const responseInterceptorId = create.config.addResponseInterceptor(response => {
|
|
400
|
+
console.log(`Response received: ${response.status}`);
|
|
401
|
+
// Transform or modify the response
|
|
402
|
+
return response;
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Add a global error interceptor (handle all errors)
|
|
406
|
+
const errorInterceptorId = create.config.addErrorInterceptor(error => {
|
|
407
|
+
console.error("Request failed:", error.message);
|
|
408
|
+
// Can throw to propagate error, or return ResponseWrapper to recover
|
|
409
|
+
throw error;
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Remove interceptors when no longer needed
|
|
413
|
+
create.config.removeRequestInterceptor(requestInterceptorId);
|
|
414
|
+
create.config.removeResponseInterceptor(responseInterceptorId);
|
|
415
|
+
create.config.removeErrorInterceptor(errorInterceptorId);
|
|
416
|
+
|
|
417
|
+
// Clear all interceptors at once
|
|
418
|
+
create.config.clearInterceptors();
|
|
419
|
+
```
|
|
278
420
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
421
|
+
#### Per-Request Interceptors
|
|
422
|
+
|
|
423
|
+
Per-request interceptors apply only to a specific request:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
// Request interceptor - modify request configuration
|
|
427
|
+
const data = await create
|
|
428
|
+
.get("https://api.example.com/users")
|
|
429
|
+
.withRequestInterceptor(config => {
|
|
430
|
+
config.headers["X-Custom-Header"] = "value";
|
|
431
|
+
config.url = "https://api.example.com/modified-url"; // Can modify URL
|
|
432
|
+
return config;
|
|
284
433
|
})
|
|
285
|
-
.
|
|
286
|
-
|
|
287
|
-
//
|
|
288
|
-
const
|
|
289
|
-
.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
keyPrefix: 'user-data'
|
|
434
|
+
.getJson();
|
|
435
|
+
|
|
436
|
+
// Response interceptor - transform response
|
|
437
|
+
const transformed = await create
|
|
438
|
+
.get("https://api.example.com/users")
|
|
439
|
+
.withResponseInterceptor(response => {
|
|
440
|
+
console.log(`Got response with status ${response.status}`);
|
|
441
|
+
return response;
|
|
294
442
|
})
|
|
295
|
-
.
|
|
296
|
-
|
|
297
|
-
//
|
|
298
|
-
const
|
|
299
|
-
.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
443
|
+
.getJson();
|
|
444
|
+
|
|
445
|
+
// Error interceptor - handle or recover from errors
|
|
446
|
+
const recovered = await create
|
|
447
|
+
.get("https://api.example.com/users")
|
|
448
|
+
.withErrorInterceptor(error => {
|
|
449
|
+
// Option 1: Throw to propagate error
|
|
450
|
+
throw error;
|
|
451
|
+
|
|
452
|
+
// Option 2: Return a ResponseWrapper to recover from error
|
|
453
|
+
// return new ResponseWrapper(fallbackResponse, error.url, error.method);
|
|
303
454
|
})
|
|
304
|
-
.
|
|
455
|
+
.getJson();
|
|
305
456
|
```
|
|
306
457
|
|
|
307
|
-
|
|
458
|
+
#### Interceptor Execution Order
|
|
308
459
|
|
|
309
|
-
|
|
460
|
+
Interceptors execute in a specific order:
|
|
461
|
+
|
|
462
|
+
1. **Request interceptors**: Global interceptors run first (in registration order), then per-request interceptors (in registration order)
|
|
463
|
+
2. **Response interceptors**: Per-request interceptors run first (in registration order), then global interceptors (in reverse registration order)
|
|
464
|
+
3. **Error interceptors**: Per-request interceptors run first (in registration order), then global interceptors (in reverse registration order)
|
|
310
465
|
|
|
311
466
|
```typescript
|
|
312
|
-
//
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
.
|
|
316
|
-
.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
467
|
+
// Request: Global 1 → Global 2 → Per-request 1 → Per-request 2
|
|
468
|
+
// Response: Per-request 1 → Per-request 2 → Global 2 → Global 1
|
|
469
|
+
const data = await create
|
|
470
|
+
.get("https://api.example.com/users")
|
|
471
|
+
.withRequestInterceptor(() => console.log("Per-request 1"))
|
|
472
|
+
.withRequestInterceptor(() => console.log("Per-request 2"))
|
|
473
|
+
.getJson();
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
#### Advanced Interceptor Patterns
|
|
477
|
+
|
|
478
|
+
```typescript
|
|
479
|
+
// Short-circuit request with early response
|
|
480
|
+
const cached = await create
|
|
481
|
+
.get("https://api.example.com/users")
|
|
482
|
+
.withRequestInterceptor(() => {
|
|
483
|
+
// Return early response from cache
|
|
484
|
+
return new Response(JSON.stringify(cachedData), {
|
|
485
|
+
status: 200,
|
|
486
|
+
headers: { "Content-Type": "application/json" },
|
|
487
|
+
});
|
|
488
|
+
})
|
|
489
|
+
.getJson();
|
|
490
|
+
|
|
491
|
+
// Recover from error with fallback
|
|
492
|
+
const fallback = await create
|
|
493
|
+
.get("https://api.example.com/users")
|
|
494
|
+
.withErrorInterceptor(error => {
|
|
495
|
+
// Return fallback response instead of throwing
|
|
496
|
+
const fallbackResponse = new Response(JSON.stringify({ users: [] }), {
|
|
497
|
+
status: 200,
|
|
498
|
+
headers: { "Content-Type": "application/json" },
|
|
499
|
+
});
|
|
500
|
+
return new ResponseWrapper(fallbackResponse, error.url, error.method);
|
|
501
|
+
})
|
|
502
|
+
.getJson();
|
|
503
|
+
|
|
504
|
+
// Async interceptors
|
|
505
|
+
const asyncData = await create
|
|
506
|
+
.get("https://api.example.com/users")
|
|
507
|
+
.withRequestInterceptor(async config => {
|
|
508
|
+
const token = await getTokenAsync();
|
|
509
|
+
config.headers["Authorization"] = `Bearer ${token}`;
|
|
510
|
+
return config;
|
|
511
|
+
})
|
|
512
|
+
.getJson();
|
|
321
513
|
```
|
|
322
514
|
|
|
323
515
|
### Request Cancellation
|
|
@@ -325,7 +517,8 @@ const products = await authRequest.sendTo('https://api.example.com/products').ge
|
|
|
325
517
|
```typescript
|
|
326
518
|
const controller = new AbortController();
|
|
327
519
|
|
|
328
|
-
const request = create
|
|
520
|
+
const request = create
|
|
521
|
+
.get("https://api.example.com/slow-endpoint")
|
|
329
522
|
.withTimeout(10000)
|
|
330
523
|
.withAbortController(controller);
|
|
331
524
|
|
|
@@ -333,151 +526,130 @@ const request = create.get()
|
|
|
333
526
|
setTimeout(() => controller.abort(), 2000);
|
|
334
527
|
|
|
335
528
|
try {
|
|
336
|
-
const data = await request.
|
|
529
|
+
const data = await request.getJson();
|
|
337
530
|
} catch (error) {
|
|
338
|
-
|
|
339
|
-
|
|
531
|
+
if (error.name === "AbortError") {
|
|
532
|
+
console.log("Request was cancelled by user");
|
|
533
|
+
} else if (error.isTimeout) {
|
|
534
|
+
console.log("Request timed out");
|
|
535
|
+
} else {
|
|
536
|
+
console.log("Other error:", error.message);
|
|
537
|
+
}
|
|
340
538
|
}
|
|
341
539
|
```
|
|
342
540
|
|
|
343
|
-
###
|
|
541
|
+
### URL Handling
|
|
542
|
+
|
|
543
|
+
The library handles both absolute and relative URLs, and automatically merges query parameters:
|
|
344
544
|
|
|
345
545
|
```typescript
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
546
|
+
// Relative URLs (preserved as-is)
|
|
547
|
+
const relative = await create.get("/api/users").getJson();
|
|
548
|
+
|
|
549
|
+
// Absolute URLs
|
|
550
|
+
const absolute = await create.get("https://api.example.com/users").getJson();
|
|
551
|
+
|
|
552
|
+
// Merging query params with existing URL params
|
|
553
|
+
const merged = await create
|
|
554
|
+
.get("https://api.example.com/users?page=1")
|
|
555
|
+
.withQueryParams({ limit: 20, sort: "name" })
|
|
556
|
+
.getJson();
|
|
557
|
+
// Result: https://api.example.com/users?page=1&limit=20&sort=name
|
|
558
|
+
|
|
559
|
+
// Special characters and unicode are properly encoded
|
|
560
|
+
const encoded = await create
|
|
561
|
+
.get("https://api.example.com/search")
|
|
562
|
+
.withQueryParams({ name: "用户名", filter: "status:active" })
|
|
563
|
+
.getJson();
|
|
564
|
+
```
|
|
351
565
|
|
|
352
|
-
|
|
353
|
-
users: User[];
|
|
354
|
-
pagination: {
|
|
355
|
-
total: number;
|
|
356
|
-
page: number;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
566
|
+
### Data Selection
|
|
359
567
|
|
|
360
|
-
|
|
568
|
+
The `getData` method provides a powerful way to extract and transform specific data from API responses:
|
|
361
569
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
.
|
|
570
|
+
```typescript
|
|
571
|
+
// Extract specific properties from nested structures
|
|
572
|
+
const posts = await create
|
|
573
|
+
.get("https://api.example.com/feed")
|
|
574
|
+
.getData(data => data.feed.posts);
|
|
366
575
|
|
|
367
|
-
//
|
|
368
|
-
const
|
|
369
|
-
.
|
|
370
|
-
.getData
|
|
576
|
+
// Transform data in the selector function
|
|
577
|
+
const usernames = await create
|
|
578
|
+
.get("https://api.example.com/users")
|
|
579
|
+
.getData(data => data.users.map(user => user.username));
|
|
371
580
|
|
|
372
|
-
//
|
|
373
|
-
const
|
|
374
|
-
.
|
|
375
|
-
.getData
|
|
581
|
+
// Apply filtering in the selector
|
|
582
|
+
const activeUsers = await create
|
|
583
|
+
.get("https://api.example.com/users")
|
|
584
|
+
.getData(data => data.users.filter(user => user.isActive));
|
|
376
585
|
|
|
377
|
-
//
|
|
378
|
-
|
|
586
|
+
// Combine data from complex nested structures
|
|
587
|
+
const combinedData = await create.get("https://api.example.com/dashboard").getData(data => ({
|
|
588
|
+
userCount: data.stats.users.total,
|
|
589
|
+
recentPosts: data.content.recent.slice(0, 5),
|
|
590
|
+
notifications: data.user.notifications.unread,
|
|
591
|
+
}));
|
|
379
592
|
```
|
|
380
593
|
|
|
381
|
-
|
|
594
|
+
When a selector fails, the error message will contain helpful context to diagnose the issue:
|
|
382
595
|
|
|
383
596
|
```typescript
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
})
|
|
394
|
-
.catch(error => {
|
|
395
|
-
console.error('Error:', error.message);
|
|
396
|
-
});
|
|
597
|
+
try {
|
|
598
|
+
// This will fail if the response structure doesn't match expectations
|
|
599
|
+
const result = await create
|
|
600
|
+
.get("https://api.example.com/data")
|
|
601
|
+
.getData(data => data.results.items);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
console.error(error);
|
|
604
|
+
// Error message includes context for debugging
|
|
605
|
+
}
|
|
397
606
|
```
|
|
398
607
|
|
|
399
|
-
|
|
608
|
+
## TypeScript Support
|
|
400
609
|
|
|
401
610
|
```typescript
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
keyGenerator: (url, method, headers) => {
|
|
409
|
-
// Include user ID from authorization header in the cache key
|
|
410
|
-
const authHeader = headers?.['Authorization'] || '';
|
|
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');
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
### Implementing Custom Storage Providers
|
|
420
|
-
|
|
421
|
-
You can create your own storage provider by implementing the `StorageProvider` interface:
|
|
611
|
+
interface User {
|
|
612
|
+
id: number;
|
|
613
|
+
name: string;
|
|
614
|
+
email: string;
|
|
615
|
+
isActive: boolean;
|
|
616
|
+
}
|
|
422
617
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
return {
|
|
429
|
-
get(key: string): string | null {
|
|
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
|
-
},
|
|
455
|
-
|
|
456
|
-
clear(): void {
|
|
457
|
-
// Only clear items in our namespace
|
|
458
|
-
Object.keys(localStorage)
|
|
459
|
-
.filter(key => key.startsWith(`${namespace}:`))
|
|
460
|
-
.forEach(key => localStorage.removeItem(key));
|
|
461
|
-
}
|
|
618
|
+
interface ApiResponse<T> {
|
|
619
|
+
data: T;
|
|
620
|
+
meta: {
|
|
621
|
+
total: number;
|
|
622
|
+
page: number;
|
|
462
623
|
};
|
|
463
624
|
}
|
|
464
625
|
|
|
465
|
-
//
|
|
466
|
-
const
|
|
467
|
-
.
|
|
468
|
-
|
|
469
|
-
ttl: 24 * 60 * 60 * 1000 // 24 hours
|
|
470
|
-
})
|
|
471
|
-
.sendTo('https://api.example.com/data');
|
|
472
|
-
```
|
|
626
|
+
// Type the full response
|
|
627
|
+
const response = await create
|
|
628
|
+
.get("https://api.example.com/users")
|
|
629
|
+
.getJson<ApiResponse<User[]>>();
|
|
473
630
|
|
|
474
|
-
|
|
631
|
+
// Or use getData with type parameters
|
|
632
|
+
const users = await create
|
|
633
|
+
.get("https://api.example.com/users")
|
|
634
|
+
.getData<ApiResponse<User[]>, User[]>(data => data.data);
|
|
475
635
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
636
|
+
// TypeScript knows the types
|
|
637
|
+
users.forEach(user => {
|
|
638
|
+
console.log(`${user.name} (${user.email}): ${user.isActive ? "Active" : "Inactive"}`);
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
// Function with proper types
|
|
642
|
+
async function getUserById(id: number): Promise<User> {
|
|
643
|
+
return create
|
|
644
|
+
.get("https://api.example.com/users")
|
|
645
|
+
.withQueryParam("id", id)
|
|
646
|
+
.getData<ApiResponse<User[]>, User>(data => {
|
|
647
|
+
const user = data.data[0];
|
|
648
|
+
if (!user) throw new Error(`User with ID ${id} not found`);
|
|
649
|
+
return user;
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
```
|
|
481
653
|
|
|
482
654
|
## CSRF Protection
|
|
483
655
|
|
|
@@ -499,11 +671,15 @@ You can configure CSRF settings globally for all requests:
|
|
|
499
671
|
|
|
500
672
|
```typescript
|
|
501
673
|
// Configure CSRF settings for all requests
|
|
502
|
-
create.config.setCsrfToken(
|
|
503
|
-
create.config.setCsrfHeaderName(
|
|
504
|
-
create.config.setXsrfCookieName(
|
|
674
|
+
create.config.setCsrfToken("your-csrf-token");
|
|
675
|
+
create.config.setCsrfHeaderName("X-CSRF-Token"); // Default header name for CSRF token
|
|
676
|
+
create.config.setXsrfCookieName("XSRF-TOKEN"); // Default cookie name to read from
|
|
677
|
+
create.config.setXsrfHeaderName("X-XSRF-TOKEN"); // Default header name for XSRF token from cookie
|
|
505
678
|
create.config.setEnableAntiCsrf(true); // Enable/disable X-Requested-With header
|
|
506
679
|
create.config.setEnableAutoXsrf(true); // Enable/disable automatic cookie-to-header token
|
|
680
|
+
|
|
681
|
+
// Reset all configuration to defaults
|
|
682
|
+
create.config.reset();
|
|
507
683
|
```
|
|
508
684
|
|
|
509
685
|
### Per-Request CSRF Settings
|
|
@@ -512,22 +688,22 @@ You can also configure CSRF protection on individual requests:
|
|
|
512
688
|
|
|
513
689
|
```typescript
|
|
514
690
|
// Configure CSRF for a specific request
|
|
515
|
-
const request = create
|
|
516
|
-
.
|
|
691
|
+
const request = create
|
|
692
|
+
.post("https://api.example.com/users")
|
|
693
|
+
.withCsrfToken("request-specific-token") // Set a specific token
|
|
517
694
|
.withAntiCsrfHeaders() // Explicitly add X-Requested-With header
|
|
518
695
|
.withoutCsrfProtection(); // Or disable all automatic CSRF protection
|
|
519
696
|
```
|
|
520
697
|
|
|
521
|
-
|
|
698
|
+
## Performance Considerations
|
|
522
699
|
|
|
523
|
-
|
|
700
|
+
create-request is designed to be lightweight and efficient:
|
|
524
701
|
|
|
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.
|
|
702
|
+
- **Zero Dependencies**: No extra libraries to load
|
|
703
|
+
- **Tree-Shakable**: Only import what you need
|
|
704
|
+
- **Minimal Overhead**: Thin wrapper around the native Fetch API
|
|
705
|
+
- **Memory Efficient**: Doesn't create unnecessary objects
|
|
706
|
+
- **Clean API**: Simple and intuitive interface
|
|
531
707
|
|
|
532
708
|
## Browser Support
|
|
533
709
|
|
|
@@ -539,6 +715,35 @@ This library works with all browsers that support the Fetch API:
|
|
|
539
715
|
- Edge 14+
|
|
540
716
|
- Opera 29+
|
|
541
717
|
|
|
718
|
+
## Comparison of JavaScript HTTP Client Libraries
|
|
719
|
+
|
|
720
|
+
| Feature | create-request | Fetch | Axios | SuperAgent | Got | Ky | node-fetch | Redaxios |
|
|
721
|
+
| ------------------- | -------------- | ------ | ------- | ---------- | ------- | ------ | ---------- | -------- |
|
|
722
|
+
| **Size (min+gzip)** | ~5.8KB | Native | ~13.6KB | ~17.8KB | ~17.8KB | ~3.4KB | ~7.7KB | ~1KB |
|
|
723
|
+
| **Browser** | Modern | Modern | IE11+ | IE9+ | ❌ No | Modern | ❌ No | Modern |
|
|
724
|
+
| **Node.js** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
725
|
+
| **HTTP/2** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
726
|
+
| **Auto Retries** | ✅ | ❌ | 🛠️ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
727
|
+
| **Cancellation** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
728
|
+
| **Auto JSON** | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
|
|
729
|
+
| **Timeout** | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
730
|
+
| **TypeScript** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
731
|
+
| **Streaming** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
|
|
732
|
+
| **Progress** | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
733
|
+
| **Middleware** | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
734
|
+
| **Cookies** | ✅ | ✅ | 🛠️ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
735
|
+
| **Pagination API** | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
736
|
+
| **Zero Deps** | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
|
|
737
|
+
| **Chainable API** | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
738
|
+
| **CSRF Protection** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
739
|
+
| **GraphQL Support** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
740
|
+
| **Interceptors** | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
|
741
|
+
|
|
742
|
+
**Notes:**
|
|
743
|
+
|
|
744
|
+
- "Modern" browser support: Chrome 42+, Firefox 39+, Safari 10.1+, Edge 14+, Opera 29+
|
|
745
|
+
- 🛠️ Feature requires additional plugins or adapters (not available out-of-the-box)
|
|
746
|
+
|
|
542
747
|
## License
|
|
543
748
|
|
|
544
749
|
MIT
|