create-request 1.5.4 → 1.6.1
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 +82 -1
- package/dist/library/BaseRequest.d.ts +58 -27
- package/dist/library/BodyRequest.d.ts +6 -7
- package/dist/library/RequestError.d.ts +64 -3
- package/dist/library/ResponseWrapper.d.ts +19 -8
- package/dist/library/apiBuilder.d.ts +20 -1
- package/dist/library/enums.d.ts +67 -58
- package/dist/library/index.cjs +537 -589
- package/dist/library/index.cjs.map +1 -1
- package/dist/library/index.d.ts +1 -1
- package/dist/library/index.esm.js +529 -589
- package/dist/library/index.esm.js.map +1 -1
- package/dist/library/index.esm.min.js +1 -1
- package/dist/library/index.esm.min.js.map +1 -1
- package/dist/library/index.min.cjs +1 -1
- package/dist/library/index.min.cjs.map +1 -1
- package/dist/library/requestMethods.d.ts +7 -14
- package/dist/library/types.d.ts +15 -0
- package/dist/library/utils/Config.d.ts +11 -11
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
- [Automatic Retries with Delay](#automatic-retries-with-delay)
|
|
26
26
|
- [Interceptors](#interceptors)
|
|
27
27
|
- [Request Cancellation](#request-cancellation)
|
|
28
|
+
- [Custom Fetch Injection](#custom-fetch-injection)
|
|
28
29
|
- [Data Selection](#data-selection)
|
|
29
30
|
- [TypeScript Support](#typescript-support)
|
|
30
31
|
- [CSRF Protection](#csrf-protection)
|
|
@@ -51,6 +52,7 @@
|
|
|
51
52
|
- 🏗️ **API Builder** - Create configured API instances with reusable default settings
|
|
52
53
|
- 🛑 **Request Cancellation** - Abort requests on demand with AbortController integration
|
|
53
54
|
- 🔌 **Interceptors** - Global and per-request interceptors for requests, responses, and errors
|
|
55
|
+
- 🧩 **Custom Fetch** - Inject any fetch-compatible function for testing, undici agents, or Next.js caching
|
|
54
56
|
- 🔷 **GraphQL Support** - Built-in GraphQL query and mutation helpers
|
|
55
57
|
|
|
56
58
|
## Why create-request?
|
|
@@ -635,6 +637,7 @@ try {
|
|
|
635
637
|
console.log(error.method); // HTTP method
|
|
636
638
|
console.log(error.isTimeout); // Whether it was a timeout
|
|
637
639
|
console.log(error.isAborted); // Whether it was aborted/cancelled
|
|
640
|
+
console.log(error.body); // Raw response body as text (if available)
|
|
638
641
|
|
|
639
642
|
// Access the original response if available
|
|
640
643
|
if (error.response) {
|
|
@@ -644,6 +647,32 @@ try {
|
|
|
644
647
|
}
|
|
645
648
|
```
|
|
646
649
|
|
|
650
|
+
#### Error Response Body
|
|
651
|
+
|
|
652
|
+
When a request fails with an HTTP error (e.g., 400, 404, 500), the response body is automatically captured and available directly on the error - no need to read it from `error.response` manually:
|
|
653
|
+
|
|
654
|
+
```typescript
|
|
655
|
+
try {
|
|
656
|
+
await create.post("https://api.example.com/users").withBody(newUser).getJson();
|
|
657
|
+
} catch (error) {
|
|
658
|
+
// Raw body as text (undefined for network errors, timeouts, and aborts)
|
|
659
|
+
console.log(error.body); // '{"message":"Email already taken","code":"DUPLICATE_EMAIL"}'
|
|
660
|
+
|
|
661
|
+
// Body parsed as JSON - never throws, returns undefined if the body isn't valid JSON
|
|
662
|
+
const details = error.getJson<{ message: string; code: string }>();
|
|
663
|
+
if (details) {
|
|
664
|
+
showToast(details.message);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
Notes on the captured body:
|
|
670
|
+
|
|
671
|
+
- `error.body` contains the raw body text whenever a response was received (HTTP errors, JSON parsing errors, GraphQL errors with `throwOnError`). For errors without a response (network failures, timeouts, aborts) it's `undefined`.
|
|
672
|
+
- `error.getJson()` lazily parses `error.body` as JSON and caches the result. It never throws - it returns `undefined` when there's no body or the body isn't valid JSON.
|
|
673
|
+
- The body is captured from a clone of the response, so `error.response` remains fully readable for backward compatibility.
|
|
674
|
+
- The captured body is also available in retry callbacks (`onRetry`, `delay`) and error interceptors.
|
|
675
|
+
|
|
647
676
|
## URL Handling
|
|
648
677
|
|
|
649
678
|
The library handles both absolute and relative URLs, and automatically merges query parameters:
|
|
@@ -923,7 +952,11 @@ const request4 = create.get("https://api.example.com/data").withRetries({
|
|
|
923
952
|
if (error.status === 429) {
|
|
924
953
|
// Check Retry-After header if available
|
|
925
954
|
const retryAfter = error.response?.headers.get("Retry-After");
|
|
926
|
-
|
|
955
|
+
if (retryAfter) return parseInt(retryAfter) * 1000;
|
|
956
|
+
|
|
957
|
+
// Or read the delay from the error response body
|
|
958
|
+
const details = error.getJson<{ retryAfterMs?: number }>();
|
|
959
|
+
return details?.retryAfterMs ?? 5000;
|
|
927
960
|
}
|
|
928
961
|
return 1000; // Default delay
|
|
929
962
|
},
|
|
@@ -1090,6 +1123,54 @@ try {
|
|
|
1090
1123
|
}
|
|
1091
1124
|
```
|
|
1092
1125
|
|
|
1126
|
+
### Custom Fetch Injection
|
|
1127
|
+
|
|
1128
|
+
By default, requests run through the global `fetch`. With `withFetch` you can inject any fetch-compatible function — per request or for a whole API instance. This unlocks testing without global mocks, custom undici agents/dispatchers in Node.js, and framework-patched fetch features like Next.js caching.
|
|
1129
|
+
|
|
1130
|
+
```typescript
|
|
1131
|
+
import create, { createApi } from "create-request";
|
|
1132
|
+
import type { FetchFunction } from "create-request";
|
|
1133
|
+
|
|
1134
|
+
// Testing: inject a stub instead of monkey-patching globalThis.fetch
|
|
1135
|
+
const stubFetch: FetchFunction = async () =>
|
|
1136
|
+
new Response(JSON.stringify({ id: 1 }), {
|
|
1137
|
+
status: 200,
|
|
1138
|
+
headers: { "content-type": "application/json" },
|
|
1139
|
+
});
|
|
1140
|
+
|
|
1141
|
+
const user = await create.get("/api/users/1").withFetch(stubFetch).getJson();
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
```typescript
|
|
1145
|
+
// Node.js: route requests through a custom undici Agent (proxies, keep-alive tuning, mTLS, ...)
|
|
1146
|
+
import { fetch as undiciFetch, Agent } from "undici";
|
|
1147
|
+
|
|
1148
|
+
const agent = new Agent({ keepAliveTimeout: 30_000, connections: 10 });
|
|
1149
|
+
|
|
1150
|
+
const api = createApi()
|
|
1151
|
+
.withBaseURL("https://api.example.com")
|
|
1152
|
+
.withFetch((url, init) => undiciFetch(url, { ...init, dispatcher: agent }));
|
|
1153
|
+
|
|
1154
|
+
const users = await api.get("/users").getJson();
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
```typescript
|
|
1158
|
+
// Next.js: pass caching hints through to the framework's patched fetch
|
|
1159
|
+
const revalidatingFetch: FetchFunction = (url, init) =>
|
|
1160
|
+
fetch(url, { ...init, next: { revalidate: 60 } });
|
|
1161
|
+
|
|
1162
|
+
const posts = await create
|
|
1163
|
+
.get("https://api.example.com/posts")
|
|
1164
|
+
.withFetch(revalidatingFetch)
|
|
1165
|
+
.getJson();
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
Notes:
|
|
1169
|
+
|
|
1170
|
+
- The custom function receives the final URL and `RequestInit` after query params, headers, and request interceptors have been applied, and it is called once per attempt when retries are configured.
|
|
1171
|
+
- It should honor `init.signal`, otherwise `withTimeout` and `withAbortController` cannot cancel the underlying work.
|
|
1172
|
+
- A per-request `withFetch` overrides one set on an API builder.
|
|
1173
|
+
|
|
1093
1174
|
### Data Selection
|
|
1094
1175
|
|
|
1095
1176
|
The `getData` method provides a powerful way to extract and transform specific data from API responses:
|
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
import { type HttpMethod, RedirectMode, RequestMode, RequestPriority, ReferrerPolicy, CredentialsPolicy } from "./enums.js";
|
|
2
|
-
import type { RetryConfig, RetryCallback, CookiesRecord, CookieOptions, RequestOptions, GraphQLOptions, ErrorInterceptor, RequestInterceptor, ResponseInterceptor } from "./types.js";
|
|
2
|
+
import type { RetryConfig, RetryCallback, CookiesRecord, CookieOptions, FetchFunction, RequestOptions, GraphQLOptions, ErrorInterceptor, RequestInterceptor, ResponseInterceptor } from "./types.js";
|
|
3
3
|
import { ResponseWrapper } from "./ResponseWrapper.js";
|
|
4
4
|
/**
|
|
5
5
|
* Base class with common functionality for all request types
|
|
6
6
|
* Provides the core request building and execution capabilities.
|
|
7
7
|
*/
|
|
8
8
|
export declare abstract class BaseRequest {
|
|
9
|
-
protected abstract
|
|
10
|
-
protected
|
|
11
|
-
protected
|
|
12
|
-
protected
|
|
13
|
-
protected
|
|
14
|
-
protected
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
private
|
|
9
|
+
protected abstract _method: HttpMethod;
|
|
10
|
+
protected _url: string;
|
|
11
|
+
protected _opts: RequestOptions;
|
|
12
|
+
protected _ctrl?: AbortController;
|
|
13
|
+
protected _fetch?: FetchFunction;
|
|
14
|
+
protected _query: URLSearchParams;
|
|
15
|
+
protected _autoCsrf: boolean;
|
|
16
|
+
private _reqI;
|
|
17
|
+
private _resI;
|
|
18
|
+
private _errI;
|
|
18
19
|
constructor(url: string);
|
|
19
20
|
/**
|
|
20
21
|
* Get GraphQL options if set (only for BodyRequest subclasses)
|
|
21
22
|
* @returns GraphQL options or undefined
|
|
22
23
|
*/
|
|
23
|
-
protected
|
|
24
|
+
protected _gql(): GraphQLOptions | undefined;
|
|
24
25
|
/**
|
|
25
26
|
* Creates a fluent API for setting enum-based options
|
|
26
27
|
* Combines direct setter with convenience methods
|
|
27
28
|
*/
|
|
28
|
-
private
|
|
29
|
-
private
|
|
29
|
+
private _fluent;
|
|
30
|
+
private _validateUrl;
|
|
30
31
|
/**
|
|
31
32
|
* Add multiple HTTP headers to the request
|
|
32
33
|
*
|
|
@@ -165,6 +166,36 @@ export declare abstract class BaseRequest {
|
|
|
165
166
|
* controller.abort();
|
|
166
167
|
*/
|
|
167
168
|
withAbortController(controller: AbortController): this;
|
|
169
|
+
/**
|
|
170
|
+
* Sets a custom fetch implementation used to execute this request.
|
|
171
|
+
* By default, requests use the global `fetch`. Injecting a custom function unlocks
|
|
172
|
+
* testing without global mocks, custom undici dispatchers/agents in Node.js,
|
|
173
|
+
* and framework-specific fetch extensions (e.g. Next.js caching options).
|
|
174
|
+
*
|
|
175
|
+
* The provided function receives the final URL and `RequestInit` (after interceptors)
|
|
176
|
+
* and must return a `Promise<Response>`. It should honor `init.signal` so that
|
|
177
|
+
* `withTimeout` and `withAbortController` keep working.
|
|
178
|
+
*
|
|
179
|
+
* @param fetchFn - A fetch-compatible function
|
|
180
|
+
* @returns The request instance for chaining
|
|
181
|
+
* @throws {RequestError} If fetchFn is not a function
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* // Testing: inject a stub instead of mocking the global fetch
|
|
185
|
+
* const stubFetch: FetchFunction = async () => new Response('{"ok":true}');
|
|
186
|
+
* const data = await create.get('/api/users').withFetch(stubFetch).getJson();
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* // Node.js: route through a custom undici agent (proxy, keep-alive tuning, ...)
|
|
190
|
+
* import { fetch as undiciFetch, Agent } from 'undici';
|
|
191
|
+
* const agent = new Agent({ keepAliveTimeout: 30_000 });
|
|
192
|
+
* request.withFetch((url, init) => undiciFetch(url, { ...init, dispatcher: agent }));
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* // Next.js: pass caching hints through to the framework's patched fetch
|
|
196
|
+
* request.withFetch((url, init) => fetch(url, { ...init, next: { revalidate: 60 } }));
|
|
197
|
+
*/
|
|
198
|
+
withFetch(fetchFn: FetchFunction): this;
|
|
168
199
|
/**
|
|
169
200
|
* Sets the referrer URL for the request. The referrer is the URL of the page that initiated the request.
|
|
170
201
|
* This can be used to override the default referrer that the browser would normally send.
|
|
@@ -507,7 +538,7 @@ export declare abstract class BaseRequest {
|
|
|
507
538
|
* Cross-environment base64 encoding
|
|
508
539
|
* Works in both browser and Node.js environments
|
|
509
540
|
*/
|
|
510
|
-
private
|
|
541
|
+
private _b64;
|
|
511
542
|
/**
|
|
512
543
|
* Sets a Bearer token for authentication.
|
|
513
544
|
* Shorthand for `withAuthorization('Bearer ' + token)`.
|
|
@@ -526,13 +557,13 @@ export declare abstract class BaseRequest {
|
|
|
526
557
|
* Safely get headers as a Record<string, string>
|
|
527
558
|
* @returns The headers object
|
|
528
559
|
*/
|
|
529
|
-
private
|
|
560
|
+
private _headers;
|
|
530
561
|
/**
|
|
531
562
|
* Helper function to check for header presence in a case-insensitive way
|
|
532
563
|
* @param headerName Header name to check
|
|
533
564
|
* @returns Boolean indicating if the header exists (case-insensitive)
|
|
534
565
|
*/
|
|
535
|
-
private
|
|
566
|
+
private _hasHeader;
|
|
536
567
|
/**
|
|
537
568
|
* Sets cookies for the request.
|
|
538
569
|
* Cookies are sent in the Cookie header. Multiple calls will merge cookies.
|
|
@@ -784,13 +815,13 @@ export declare abstract class BaseRequest {
|
|
|
784
815
|
/**
|
|
785
816
|
* Apply CSRF protection headers based on configuration
|
|
786
817
|
*/
|
|
787
|
-
private
|
|
818
|
+
private _applyCsrf;
|
|
788
819
|
/**
|
|
789
820
|
* Formats the URL with any query parameters
|
|
790
821
|
* @param url The base URL
|
|
791
822
|
* @returns The URL with query parameters appended
|
|
792
823
|
*/
|
|
793
|
-
private
|
|
824
|
+
private _fullUrl;
|
|
794
825
|
/**
|
|
795
826
|
* Executes a request with configured retry logic
|
|
796
827
|
* @param url The formatted URL to send the request to
|
|
@@ -798,42 +829,42 @@ export declare abstract class BaseRequest {
|
|
|
798
829
|
* @returns A wrapped response object
|
|
799
830
|
* @throws RequestError if the request fails after all retries
|
|
800
831
|
*/
|
|
801
|
-
private
|
|
832
|
+
private _retry;
|
|
802
833
|
/**
|
|
803
834
|
* Run request interceptors in order: global interceptors first, then per-request
|
|
804
835
|
* @param configParam - The request configuration
|
|
805
836
|
* @returns Modified config or a Response to short-circuit
|
|
806
837
|
*/
|
|
807
|
-
private
|
|
838
|
+
private _runReqI;
|
|
808
839
|
/**
|
|
809
840
|
* Run response interceptors in reverse order: per-request interceptors first, then global in reverse
|
|
810
841
|
* @param response - The response wrapper
|
|
811
842
|
* @returns Modified response wrapper
|
|
812
843
|
*/
|
|
813
|
-
private
|
|
844
|
+
private _runResI;
|
|
814
845
|
/**
|
|
815
846
|
* Run error interceptors in reverse order: per-request interceptors first, then global in reverse
|
|
816
847
|
* @param error - The error that occurred
|
|
817
848
|
* @returns Modified error or a ResponseWrapper to recover
|
|
818
849
|
*/
|
|
819
|
-
private
|
|
850
|
+
private _runErrI;
|
|
820
851
|
/**
|
|
821
852
|
* Helper to create an abort signal with timeout support
|
|
822
853
|
* Handles various AbortSignal API levels gracefully
|
|
823
854
|
*/
|
|
824
|
-
private
|
|
855
|
+
private _signal;
|
|
825
856
|
/**
|
|
826
857
|
* Manually combine two abort signals for older environments
|
|
827
858
|
* Returns the first signal and listens to the second
|
|
828
859
|
*/
|
|
829
|
-
private
|
|
860
|
+
private _combine;
|
|
830
861
|
/**
|
|
831
862
|
* Convert fetchOptions to RequestConfig with proper typing
|
|
832
863
|
*/
|
|
833
|
-
private
|
|
864
|
+
private _config;
|
|
834
865
|
/**
|
|
835
866
|
* Apply interceptor results back to fetchOptions
|
|
836
867
|
*/
|
|
837
|
-
private
|
|
838
|
-
private
|
|
868
|
+
private _applyConfig;
|
|
869
|
+
private _run;
|
|
839
870
|
}
|
|
@@ -5,10 +5,9 @@ import type { ResponseWrapper } from "./ResponseWrapper.js";
|
|
|
5
5
|
* Base class for requests that can have a body (POST, PUT, PATCH)
|
|
6
6
|
*/
|
|
7
7
|
export declare abstract class BodyRequest extends BaseRequest {
|
|
8
|
-
protected
|
|
9
|
-
private
|
|
10
|
-
private
|
|
11
|
-
constructor(url: string);
|
|
8
|
+
protected _body?: Body;
|
|
9
|
+
private _bodyType?;
|
|
10
|
+
private _gqlOpts;
|
|
12
11
|
/**
|
|
13
12
|
* Sets the request body. Automatically detects the body type and sets appropriate Content-Type header.
|
|
14
13
|
* Supports JSON objects/arrays, strings, FormData, Blob, ArrayBuffer, URLSearchParams, and ReadableStream.
|
|
@@ -86,13 +85,13 @@ export declare abstract class BodyRequest extends BaseRequest {
|
|
|
86
85
|
/**
|
|
87
86
|
* Check if Content-Type header is already set (case-insensitive)
|
|
88
87
|
*/
|
|
89
|
-
private
|
|
90
|
-
private
|
|
88
|
+
private _hasCT;
|
|
89
|
+
private _setCT;
|
|
91
90
|
/**
|
|
92
91
|
* Get the GraphQL options if set
|
|
93
92
|
* @returns The GraphQL options or undefined
|
|
94
93
|
*/
|
|
95
|
-
protected
|
|
94
|
+
protected _gql(): GraphQLOptions | undefined;
|
|
96
95
|
/**
|
|
97
96
|
* Execute the request and return the ResponseWrapper
|
|
98
97
|
* Overrides the base implementation to add body handling
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract a message from an unknown thrown value
|
|
3
|
+
* @internal
|
|
4
|
+
*/
|
|
5
|
+
export declare const errorMessage: (e: unknown) => string;
|
|
6
|
+
/**
|
|
7
|
+
* Coerce an unknown thrown value to an Error
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare const toError: (e: unknown) => Error;
|
|
1
11
|
/**
|
|
2
12
|
* Error class for HTTP request failures.
|
|
3
13
|
* Extends the standard Error class with additional context about the failed request.
|
|
@@ -11,6 +21,8 @@
|
|
|
11
21
|
* console.log(`URL: ${error.url}`);
|
|
12
22
|
* console.log(`Method: ${error.method}`);
|
|
13
23
|
* console.log(`Status: ${error.status}`);
|
|
24
|
+
* console.log(`Body: ${error.body}`); // Raw response body (if available)
|
|
25
|
+
* console.log(error.getJson()); // Body parsed as JSON (or undefined)
|
|
14
26
|
* console.log(`Is timeout: ${error.isTimeout}`);
|
|
15
27
|
* console.log(`Is aborted: ${error.isAborted}`);
|
|
16
28
|
* }
|
|
@@ -21,6 +33,12 @@ export declare class RequestError extends Error {
|
|
|
21
33
|
readonly status?: number;
|
|
22
34
|
/** The Response object if the request received a response before failing */
|
|
23
35
|
readonly response?: Response;
|
|
36
|
+
/**
|
|
37
|
+
* The raw response body as text, if a response was received and its body could be read.
|
|
38
|
+
* `undefined` for errors without a response (network errors, timeouts, aborts)
|
|
39
|
+
* or when the body could not be read.
|
|
40
|
+
*/
|
|
41
|
+
readonly body?: string;
|
|
24
42
|
/** The URL that was requested */
|
|
25
43
|
readonly url: string;
|
|
26
44
|
/** The HTTP method that was used (e.g., 'GET', 'POST') */
|
|
@@ -29,6 +47,8 @@ export declare class RequestError extends Error {
|
|
|
29
47
|
readonly isTimeout: boolean;
|
|
30
48
|
/** Whether the request was aborted (cancelled) */
|
|
31
49
|
readonly isAborted: boolean;
|
|
50
|
+
/** Cached result of parsing `body` as JSON (lazily populated by getJson) */
|
|
51
|
+
private _parsed?;
|
|
32
52
|
/**
|
|
33
53
|
* Creates a new RequestError instance.
|
|
34
54
|
*
|
|
@@ -38,6 +58,7 @@ export declare class RequestError extends Error {
|
|
|
38
58
|
* @param options - Additional error context
|
|
39
59
|
* @param options.status - HTTP status code if available
|
|
40
60
|
* @param options.response - The Response object if available
|
|
61
|
+
* @param options.body - The raw response body as text, if available
|
|
41
62
|
* @param options.isTimeout - Whether this was a timeout error
|
|
42
63
|
* @param options.isAborted - Whether the request was aborted
|
|
43
64
|
* @param options.cause - The underlying error that caused this error
|
|
@@ -45,10 +66,48 @@ export declare class RequestError extends Error {
|
|
|
45
66
|
constructor(message: string, url: string, method: string, options?: {
|
|
46
67
|
status?: number;
|
|
47
68
|
response?: Response;
|
|
69
|
+
body?: string;
|
|
48
70
|
isTimeout?: boolean;
|
|
49
71
|
isAborted?: boolean;
|
|
50
72
|
cause?: Error;
|
|
51
73
|
});
|
|
74
|
+
/**
|
|
75
|
+
* Parses the captured response body (`body`) as JSON.
|
|
76
|
+
* The result is cached, so repeated calls don't re-parse.
|
|
77
|
+
* This method never throws - it returns `undefined` when there is no body
|
|
78
|
+
* or the body is not valid JSON, making it safe to use in error handlers.
|
|
79
|
+
*
|
|
80
|
+
* @returns The parsed JSON body, or `undefined` if no body was captured or it isn't valid JSON
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* try {
|
|
85
|
+
* await create.post('/api/users').withBody(user).getJson();
|
|
86
|
+
* } catch (error) {
|
|
87
|
+
* if (error instanceof RequestError) {
|
|
88
|
+
* const details = error.getJson<{ message: string; code: string }>();
|
|
89
|
+
* console.log(details?.message ?? error.body ?? error.message);
|
|
90
|
+
* }
|
|
91
|
+
* }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
getJson<T = unknown>(): T | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Safely reads the body of a Response as text without consuming it.
|
|
97
|
+
* The response is cloned before reading, so the original body remains readable.
|
|
98
|
+
* Never throws - returns `undefined` if the body is unavailable or cannot be read
|
|
99
|
+
* (e.g., already consumed, locked stream, or read failure).
|
|
100
|
+
*
|
|
101
|
+
* @param response - The Response to read the body from
|
|
102
|
+
* @returns The body as text, or `undefined` if it could not be read
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const body = await RequestError.captureBody(response);
|
|
107
|
+
* throw RequestError.fromResponse(response, url, 'GET', body);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
static captureBody(response: Response): Promise<string | undefined>;
|
|
52
111
|
/**
|
|
53
112
|
* Creates a RequestError for a timeout failure.
|
|
54
113
|
*
|
|
@@ -70,17 +129,19 @@ export declare class RequestError extends Error {
|
|
|
70
129
|
* @param response - The Response object from the failed request
|
|
71
130
|
* @param url - The URL that was requested
|
|
72
131
|
* @param method - The HTTP method that was used
|
|
73
|
-
* @
|
|
132
|
+
* @param body - The response body as text, if already read (see {@link RequestError.captureBody})
|
|
133
|
+
* @returns A RequestError with the status code, response object, and body (if provided)
|
|
74
134
|
*
|
|
75
135
|
* @example
|
|
76
136
|
* ```typescript
|
|
77
137
|
* const response = await fetch('/api/users');
|
|
78
138
|
* if (!response.ok) {
|
|
79
|
-
*
|
|
139
|
+
* const body = await RequestError.captureBody(response);
|
|
140
|
+
* throw RequestError.fromResponse(response, '/api/users', 'GET', body);
|
|
80
141
|
* }
|
|
81
142
|
* ```
|
|
82
143
|
*/
|
|
83
|
-
static fromResponse(response: Response, url: string, method: string): RequestError;
|
|
144
|
+
static fromResponse(response: Response, url: string, method: string, body?: string): RequestError;
|
|
84
145
|
/**
|
|
85
146
|
* Creates a RequestError from a network-level error.
|
|
86
147
|
* Automatically detects and categorizes common network errors (timeouts, DNS errors, connection errors).
|
|
@@ -18,12 +18,12 @@ export declare class ResponseWrapper {
|
|
|
18
18
|
readonly url?: string;
|
|
19
19
|
/** The HTTP method that was used (if available) */
|
|
20
20
|
readonly method?: string;
|
|
21
|
-
private readonly
|
|
22
|
-
private
|
|
23
|
-
private
|
|
24
|
-
private
|
|
25
|
-
private
|
|
26
|
-
private
|
|
21
|
+
private readonly _res;
|
|
22
|
+
private _gqlOpts?;
|
|
23
|
+
private _blob?;
|
|
24
|
+
private _text?;
|
|
25
|
+
private _json?;
|
|
26
|
+
private _buf?;
|
|
27
27
|
constructor(response: Response, url?: string, method?: string, graphQLOptions?: GraphQLOptions);
|
|
28
28
|
/**
|
|
29
29
|
* HTTP status code (e.g., 200, 404, 500)
|
|
@@ -46,17 +46,28 @@ export declare class ResponseWrapper {
|
|
|
46
46
|
* Use this if you need direct access to the underlying Response.
|
|
47
47
|
*/
|
|
48
48
|
get raw(): Response;
|
|
49
|
+
/**
|
|
50
|
+
* Create a RequestError carrying this response's context
|
|
51
|
+
* @param message - The error message
|
|
52
|
+
* @param withBody - Whether to attach the cached body text to the error
|
|
53
|
+
*/
|
|
54
|
+
private _err;
|
|
55
|
+
/**
|
|
56
|
+
* Read the response body via the given reader, wrapping failures in a RequestError
|
|
57
|
+
* @throws RequestError if the body has already been consumed or reading fails
|
|
58
|
+
*/
|
|
59
|
+
private _read;
|
|
49
60
|
/**
|
|
50
61
|
* Check if the response body has already been consumed and throw an error if so
|
|
51
62
|
* @throws RequestError if the body has already been consumed
|
|
52
63
|
*/
|
|
53
|
-
private
|
|
64
|
+
private _checkUsed;
|
|
54
65
|
/**
|
|
55
66
|
* Check for GraphQL errors and throw if throwOnError is enabled
|
|
56
67
|
* @param data - The parsed JSON data
|
|
57
68
|
* @throws RequestError if GraphQL response contains errors and throwOnError is enabled
|
|
58
69
|
*/
|
|
59
|
-
private
|
|
70
|
+
private _checkGql;
|
|
60
71
|
/**
|
|
61
72
|
* Parse the response body as JSON
|
|
62
73
|
* If GraphQL options are set with throwOnError=true, will check for GraphQL errors and throw.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GetRequest, PostRequest, PutRequest, DeleteRequest, PatchRequest, HeadRequest, OptionsRequest } from "./requestMethods.js";
|
|
2
|
-
import type { RetryConfig, RetryCallback, CookiesRecord, CookieOptions, RequestInterceptor, ResponseInterceptor, ErrorInterceptor } from "./types.js";
|
|
2
|
+
import type { RetryConfig, RetryCallback, CookiesRecord, CookieOptions, FetchFunction, RequestInterceptor, ResponseInterceptor, ErrorInterceptor } from "./types.js";
|
|
3
3
|
import type { CredentialsPolicy, RedirectMode, RequestPriority, ReferrerPolicy, RequestMode } from "./enums.js";
|
|
4
4
|
/**
|
|
5
5
|
* API Builder for creating configured API instances with reusable default settings.
|
|
@@ -263,6 +263,25 @@ type ApiBuilder = {
|
|
|
263
263
|
* ```
|
|
264
264
|
*/
|
|
265
265
|
withCache(cache: RequestCache): ApiBuilder;
|
|
266
|
+
/**
|
|
267
|
+
* Sets a custom fetch implementation for all requests created through this API instance.
|
|
268
|
+
* Useful for injecting test stubs, undici agents/dispatchers, or framework-patched
|
|
269
|
+
* fetch functions (e.g. Next.js caching options). Individual requests can still
|
|
270
|
+
* override it with their own `withFetch` call.
|
|
271
|
+
*
|
|
272
|
+
* @param fetchFn - A fetch-compatible function
|
|
273
|
+
* @returns The API builder instance for chaining
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```typescript
|
|
277
|
+
* import { fetch as undiciFetch, Agent } from 'undici';
|
|
278
|
+
* const agent = new Agent({ connections: 10 });
|
|
279
|
+
* const api = createApi()
|
|
280
|
+
* .withBaseURL('https://api.example.com')
|
|
281
|
+
* .withFetch((url, init) => undiciFetch(url, { ...init, dispatcher: agent }));
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
withFetch(fetchFn: FetchFunction): ApiBuilder;
|
|
266
285
|
/**
|
|
267
286
|
* Set the request mode.
|
|
268
287
|
* Controls CORS behavior and what types of responses are allowed.
|