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 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
- return retryAfter ? parseInt(retryAfter) * 1000 : 5000;
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 method: HttpMethod;
10
- protected url: string;
11
- protected requestOptions: RequestOptions;
12
- protected abortController?: AbortController;
13
- protected queryParams: URLSearchParams;
14
- protected autoApplyCsrfProtection: boolean;
15
- private requestInterceptors;
16
- private responseInterceptors;
17
- private errorInterceptors;
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 getGraphQLOptions(): GraphQLOptions | undefined;
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 createFluentSetter;
29
- private validateUrl;
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 encodeBase64;
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 getHeadersRecord;
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 hasHeader;
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 applyCsrfProtection;
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 formatUrlWithQueryParams;
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 executeWithRetries;
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 runRequestInterceptors;
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 runResponseInterceptors;
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 runErrorInterceptors;
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 createAbortSignal;
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 combineSignalsManually;
860
+ private _combine;
830
861
  /**
831
862
  * Convert fetchOptions to RequestConfig with proper typing
832
863
  */
833
- private createRequestConfig;
864
+ private _config;
834
865
  /**
835
866
  * Apply interceptor results back to fetchOptions
836
867
  */
837
- private applyRequestConfig;
838
- private executeRequest;
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 body?: Body;
9
- private bodyType?;
10
- private graphQLOptions;
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 hasContentType;
90
- private setContentTypeIfNeeded;
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 getGraphQLOptions(): GraphQLOptions | undefined;
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
- * @returns A RequestError with the status code and response object
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
- * throw RequestError.fromResponse(response, '/api/users', 'GET');
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 response;
22
- private graphQLOptions?;
23
- private cachedBlob?;
24
- private cachedText?;
25
- private cachedJson?;
26
- private cachedArrayBuffer?;
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 checkBodyNotConsumed;
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 checkGraphQLErrors;
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.