@superutils/fetch 1.5.14 → 1.5.16

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
@@ -14,25 +14,26 @@ For full API reference check out the [docs page](https://alien45.github.io/super
14
14
 
15
15
  - [Features](#features)
16
16
  - [Installation](#installation)
17
- - [NPM](#npm)
18
- - [CDN / Browser](#cdn--browser)
19
- - [Defaults](#defaults)
20
- - [Timeout](#timeout)
21
- - [Conent Type](#content-type)
22
- - [Response Parsing](#fetch-as)
17
+ - [NPM](#npm)
18
+ - [CDN / Browser](#cdn--browser)
19
+ - [Defaults](#defaults)
20
+ - [Timeout](#timeout)
21
+ - [Conent Type](#content-type)
22
+ - [Response Parsing](#fetch-as)
23
23
  - [Usage](#usage)
24
- - [`fetch()`](#fetch): drop-in replacement for built-in `fetch()`
25
- - [`TimeoutPromise` Instance](#promise-features): finer control over the request
26
- - [`Method Specific Functions`](#methods)
27
- - [`fetch.get()`](#fetch-get)
28
- - [`fetch.get.deferred()`](#fetch-deferred): cancellable and debounced or throttled `fetch()`
29
- - [`fetch.post()`](#post): make post requests
30
- - [`fetch.post.deferred()`](#post-deferred): cancellable and debounced or throttled `post()`
31
- - [`Interceptors/Transformers`](#interceptors)
32
- - [`Retry`](#retry) Retry on request failure
33
- - [`Timeout`](#timeout) Abort request on timeout
34
- - [`createClient()`](#create-client)
35
- - [`createPostClient()`](#create-post-client)
24
+ - [`fetch()`](#fetch): drop-in replacement for built-in `fetch()`
25
+ - [`TimeoutPromise` Instance](#promise-features): finer control over the request
26
+ - [`Method Specific Functions`](#methods)
27
+ - [`fetch.get()`](#fetch-get)
28
+ - [`fetch.get.deferred()`](#fetch-deferred): cancellable and debounced or throttled `fetch()`
29
+ - [`fetch.post()`](#post): make post requests
30
+ - [`fetch.post.deferred()`](#post-deferred): cancellable and debounced or throttled `post()`
31
+ - [`Interceptors/Transformers`](#interceptors)
32
+ - [`Retry`](#retry) Retry on request failure
33
+ - [`Timeout`](#timeout) Abort request on timeout
34
+ - [`createClient()`](#create-client):
35
+ - [`createPostClient()`](#create-post-client):
36
+ - [`fetchFunc`](#fetch-func): Using with third-party libraries (e.g., Axios)
36
37
 
37
38
  ## Features
38
39
 
@@ -141,8 +142,8 @@ To retrieve the response in a different format (e.g., as text, a blob, or the ra
141
142
  import fetch, { FetchAs } from '@superutils/fetch'
142
143
 
143
144
  fetch
144
- .get('[DUMMYJSON-DOT-COM]/products/1', { as: FetchAs.text })
145
- .then(console.log)
145
+ .get('[DUMMYJSON-DOT-COM]/products/1', { as: FetchAs.text })
146
+ .then(console.log)
146
147
  ```
147
148
 
148
149
  > **Note:** To ensure type safety, the `as` property is excluded from `fetch.defaults` in TypeScript. Since this option determines the function's return type, setting it globally would prevent accurate type inference for individual requests.
@@ -159,8 +160,8 @@ Use as a drop-in replacement to built-in `fetch()`.
159
160
  import fetch from '@superutils/fetch'
160
161
 
161
162
  fetch('[DUMMYJSON-DOT-COM]/products/1')
162
- .then(response => response.json())
163
- .then(console.log)
163
+ .then(response => response.json())
164
+ .then(console.log)
164
165
  ```
165
166
 
166
167
  <div id="promise-features"></div>
@@ -179,11 +180,11 @@ const request = fetch('[DUMMYJSON-DOT-COM]/products/1')
179
180
  console.log(request.pending) // true
180
181
 
181
182
  request.then(() => {
182
- console.log(request.resolved) // true
183
- console.log(request.pending) // false
184
- console.log(request.rejected) // false
185
- console.log(request.aborted) // false
186
- console.log(request.timedout) // false
183
+ console.log(request.resolved) // true
184
+ console.log(request.pending) // false
185
+ console.log(request.rejected) // false
186
+ console.log(request.aborted) // false
187
+ console.log(request.timedout) // false
187
188
  })
188
189
  ```
189
190
 
@@ -200,7 +201,7 @@ request.then(result => console.log(result), console.warn)
200
201
  // Add a callback to do stuff whenever request is aborted externally.
201
202
  // This will not be invoked if fetch fails or resolves (promise finalized naturally) using the Promise executor.
202
203
  request.onEarlyFinalize.push((resolved, valueOrReason) =>
203
- console.log('Aborted externally:', { resolved, valueOrReason }),
204
+ console.log('Aborted externally:', { resolved, valueOrReason }),
204
205
  )
205
206
 
206
207
  // resolve/reject before the promise is finalized
@@ -260,8 +261,8 @@ Equivalent to `fetch(url, { method: 'get', as: 'json' })`.
260
261
  import fetch from '@superutils/fetch'
261
262
 
262
263
  fetch
263
- .get('[DUMMYJSON-DOT-COM]/products/1')
264
- .then(product => console.log({ product }))
264
+ .get('[DUMMYJSON-DOT-COM]/products/1')
265
+ .then(product => console.log({ product }))
265
266
  ```
266
267
 
267
268
  <div id="fetch-deferred"></div>
@@ -275,22 +276,22 @@ import fetch from '@superutils/fetch'
275
276
 
276
277
  // Create a debounced search function with a 300ms delay.
277
278
  const searchProducts = fetch.get.deferred({
278
- delay: 300, // Debounce delay
279
- resolveIgnored: 'WITH_UNDEFINED', // Ignored (aborted) promises will resolve with `undefined`
279
+ delay: 300, // Debounce delay
280
+ resolveIgnored: 'WITH_UNDEFINED', // Ignored (aborted) promises will resolve with `undefined`
280
281
  })
281
282
 
282
283
  // User types 'iphone'
283
284
  searchProducts('[DUMMYJSON-DOT-COM]/products/search?q=iphone').then(result => {
284
- console.log('Result for "iphone":', result)
285
+ console.log('Result for "iphone":', result)
285
286
  })
286
287
 
287
288
  // Before 300ms has passed, the user continues typing 'iphone 12'
288
289
  setTimeout(() => {
289
- searchProducts('[DUMMYJSON-DOT-COM]/products/search?q=iphone 12').then(
290
- result => {
291
- console.log('Result for "iphone 12":', result)
292
- },
293
- )
290
+ searchProducts('[DUMMYJSON-DOT-COM]/products/search?q=iphone 12').then(
291
+ result => {
292
+ console.log('Result for "iphone 12":', result)
293
+ },
294
+ )
294
295
  }, 200)
295
296
  // Outcome:
296
297
  // The first request for "iphone" is aborted.
@@ -305,18 +306,18 @@ setTimeout(() => {
305
306
  - **`delay: 0`**: Disables debouncing and throttling, enabling sequential/queue mode. Both requests ("iphone"
306
307
  and "iphone 12") would execute, but one after the other, never simultaneously.
307
308
  - **`resolveIgnored` (enum)**: Controls how the promise for an aborted request (like the first "iphone" call) resolves.
308
- 1. `ResolveIgnored.WITH_UNDEFINED` (used in the example): The promise for the aborted "iphone"
309
- request resolves with `undefined`.
310
- 2. `ResolveIgnored.WITH_LAST`: The promise for the aborted "iphone" request waits and resolves with the result
311
- of the final "iphone 12" request. Both promises resolve to the same value.
312
- 3. `ResolveIgnored.NEVER`: The promise for the aborted "iphone" request is neither resolved nor rejected.
313
- It will remain pending indefinitely.
309
+ 1. `ResolveIgnored.WITH_UNDEFINED` (used in the example): The promise for the aborted "iphone"
310
+ request resolves with `undefined`.
311
+ 2. `ResolveIgnored.WITH_LAST`: The promise for the aborted "iphone" request waits and resolves with the result
312
+ of the final "iphone 12" request. Both promises resolve to the same value.
313
+ 3. `ResolveIgnored.NEVER`: The promise for the aborted "iphone" request is neither resolved nor rejected.
314
+ It will remain pending indefinitely.
314
315
  - **`resolveError` (enum)**: Controls how failed requests are handled.
315
- 1. `ResolveError.NEVER`: The promise for a failed request will neither resolve nor reject, causing it to remain pending indefinitely.
316
- > **Warning:** Use with caution, as this may lead to memory leaks if not handled properly.
317
- 2. `ResolveError.WITH_ERROR`: The promise resolves with the `FetchError` object instead of being rejected.
318
- 3. `ResolveError.WITH_UNDEFINED`: The promise resolves with an `undefined` value upon failure.
319
- 4. `ResolveError.REJECT`: (Default) The promise is rejected with a `FetchError`, adhering to standard promise behavior.
316
+ 1. `ResolveError.NEVER`: The promise for a failed request will neither resolve nor reject, causing it to remain pending indefinitely.
317
+ > **Warning:** Use with caution, as this may lead to memory leaks if not handled properly.
318
+ 2. `ResolveError.WITH_ERROR`: The promise resolves with the `FetchError` object instead of being rejected.
319
+ 3. `ResolveError.WITH_UNDEFINED`: The promise resolves with an `undefined` value upon failure.
320
+ 4. `ResolveError.REJECT`: (Default) The promise is rejected with a `FetchError`, adhering to standard promise behavior.
320
321
 
321
322
  <!-- #### Using defaults to reduce redundancy
322
323
 
@@ -360,8 +361,8 @@ import fetch from '@superutils/fetch'
360
361
  const newProduct = { title: 'Perfume Oil' }
361
362
 
362
363
  fetch.post('[DUMMYJSON-DOT-COM]/products/add', newProduct).then(
363
- createdProduct => console.log('Product created:', createdProduct),
364
- error => console.error('Failed to create product:', error),
364
+ createdProduct => console.log('Product created:', createdProduct),
365
+ error => console.error('Failed to create product:', error),
365
366
  )
366
367
  ```
367
368
 
@@ -379,13 +380,13 @@ import PromisE from '@superutils/promise'
379
380
 
380
381
  // Create a throttled function to auto-save product updates.
381
382
  const saveProductThrottled = fetch.post.deferred(
382
- {
383
- delay: 1000, // Throttle window of 1 second
384
- throttle: true,
385
- trailing: true, // Ensures the very last update is always saved
386
- onResult: product => console.log(`[Saved] Product: ${product.title}`),
387
- },
388
- '[DUMMYJSON-DOT-COM]/products/add', // Default URL
383
+ {
384
+ delay: 1000, // Throttle window of 1 second
385
+ throttle: true,
386
+ trailing: true, // Ensures the very last update is always saved
387
+ onResult: product => console.log(`[Saved] Product: ${product.title}`),
388
+ },
389
+ '[DUMMYJSON-DOT-COM]/products/add', // Default URL
389
390
  )
390
391
  // Simulate a user typing quickly, triggering multiple saves.
391
392
  console.log('User starts typing...')
@@ -417,41 +418,40 @@ let currentRefreshToken = ''
417
418
  // Create a debounced function to refresh the auth token.
418
419
  // It waits 300ms after the last call before executing.
419
420
  const requestNewToken = fetch.post.deferred(
420
- {
421
- delay: 300, // debounce delay
422
- onResult: ({ refreshToken = '' }) => {
423
- console.log(
424
- `Auth token successfully refreshed at ${new Date().toISOString()}`,
425
- )
426
- currentRefreshToken = refreshToken
427
- },
428
- },
429
- '[DUMMYJSON-DOT-COM]/auth/refresh', // Default URL
430
- () => ({
431
- refreshToken: currentRefreshToken,
432
- expiresInMins: 30,
433
- }),
421
+ {
422
+ delay: 300, // debounce delay
423
+ onResult: ({ refreshToken = '' }) => {
424
+ console.log(
425
+ `Auth token successfully refreshed at ${new Date().toISOString()}`,
426
+ )
427
+ currentRefreshToken = refreshToken
428
+ },
429
+ },
430
+ '[DUMMYJSON-DOT-COM]/auth/refresh', // Default URL
431
+ () => ({
432
+ refreshToken: currentRefreshToken,
433
+ expiresInMins: 30,
434
+ }),
434
435
  )
435
436
 
436
- // First authenticate user to get the initial refresh token and then request new referesh tokens
437
437
  // First authenticate user to get the initial refresh token and then request new refresh tokens
438
438
  fetch
439
- .post<{ refreshToken: string }>(
440
- '[DUMMYJSON-DOT-COM]/auth/login',
441
- {
442
- username: 'emilys',
443
- password: 'emilyspass',
444
- expiresInMins: 30,
445
- },
446
- { credentials: 'include' },
447
- )
448
- .then(result => {
449
- currentRefreshToken = result?.refreshToken
450
-
451
- requestNewToken() // Called at 0ms
452
- PromisE.delay(50, requestNewToken) // Called at 50ms
453
- PromisE.delay(100, requestNewToken) // Called at 100ms
454
- }, console.error)
439
+ .post<{ refreshToken: string }>(
440
+ '[DUMMYJSON-DOT-COM]/auth/login',
441
+ {
442
+ username: 'emilys',
443
+ password: 'emilyspass',
444
+ expiresInMins: 30,
445
+ },
446
+ { credentials: 'include' },
447
+ )
448
+ .then(result => {
449
+ currentRefreshToken = result?.refreshToken
450
+
451
+ requestNewToken() // Called at 0ms
452
+ PromisE.delay(50, requestNewToken) // Called at 50ms
453
+ PromisE.delay(100, requestNewToken) // Called at 100ms
454
+ }, console.error)
455
455
  // Outcome:
456
456
  // The first two calls are aborted by the debounce mechanism.
457
457
  // Only the final call executes, 300ms after it was made (at the 400ms mark).
@@ -467,8 +467,8 @@ The following interceptor callbacks allow intercepting and/or transforming at di
467
467
  #### Interceptor types (executed in sequence):
468
468
 
469
469
  1. `request`: Request interceptors are executed before a HTTP request is made.
470
- - To transform the URL simply return a new or modified URL.
471
- - To transform `fetch` options simply modify the options parameter
470
+ - To transform the URL simply return a new or modified URL.
471
+ - To transform `fetch` options simply modify the options parameter
472
472
  2. `response`: Response interceptors are executed after receiving a `fetch` Response regardless of the HTTP status code.
473
473
  3. `result`: Result interceptors are executed before returning the result. To transform the result simply return a new value.
474
474
  PS: if the value of `options.as` is `FetchAs.response` (`"response"`), the value received in result will be a `Response` object.
@@ -480,8 +480,8 @@ The following interceptor callbacks allow intercepting and/or transforming at di
480
480
  - If an exception is raised while executing the interceptors, it will be gracefully ignored.
481
481
  - Value returned (transformed) by an interceptor will be carried over to the subsequent interceptor of the same type.
482
482
  - There are 2 category of interceptors:
483
- - Local: interceptors provided when making a request.
484
- - Global: interceptors that are executed application-wide on every request. Global interceptors can be added/accessed at `fetch.defaults.interceptors`. Global interceptors are always executed before local interceptors.
483
+ - Local: interceptors provided when making a request.
484
+ - Global: interceptors that are executed application-wide on every request. Global interceptors can be added/accessed at `fetch.defaults.interceptors`. Global interceptors are always executed before local interceptors.
485
485
 
486
486
  **Example: Interceptor usage**
487
487
 
@@ -489,51 +489,48 @@ The following interceptor callbacks allow intercepting and/or transforming at di
489
489
  import fetch, { FetchError } from '@superutils/fetch'
490
490
 
491
491
  const interceptors = {
492
- error: [
493
- (err, url, options) => {
494
- console.log('Request failed', err, url, options)
495
- // return nothing/undefined to keep the error unchanged
496
- // or return modified/new error
497
- err.message = 'My custom error message!'
498
- // or create a new FetchError by cloning it (make sure all the required properties are set correctly)
499
- return err.clone('My custom error message!')
500
- },
501
- ],
502
- request: [
503
- (url, options) => {
504
- // add extra headers or modify request options here
505
- options.headers.append('x-custom-header', 'some value')
506
-
507
- // transform the URL by returning a modified URL
508
- return url + '?param=value'
509
- },
510
- ],
511
- response: [
512
- (response, url, options) => {
513
- if (response.ok) return
514
- console.log('request was successful', { url, options })
515
-
516
- // You can transform the response by returning different `Response` object or even make a completely new HTTP reuqest.
517
- // You can transform the response by returning different `Response` object or even make a completely new HTTP request.
518
- // The subsequent response interceptors will receive the returned response
519
- return fetch('[DUMMYJSON-DOT-COM]/products/1') // promise will be resolved automatically
520
- },
521
- ],
522
- result: [
523
- (result, url, options) => {
524
- const productId = Number(
525
- new URL(url).pathname.split('/products/')[1],
526
- )
527
- if (options.method === 'get' && !Number.isNaN(productId)) {
528
- result.title ??= 'Unknown title'
529
- }
530
- return result
531
- },
532
- ],
492
+ error: [
493
+ (err, url, options) => {
494
+ console.log('Request failed', err, url, options)
495
+ // return nothing/undefined to keep the error unchanged
496
+ // or return modified/new error
497
+ err.message = 'My custom error message!'
498
+ // or create a new FetchError by cloning it (make sure all the required properties are set correctly)
499
+ return err.clone('My custom error message!')
500
+ },
501
+ ],
502
+ request: [
503
+ (url, options) => {
504
+ // add extra headers or modify request options here
505
+ options.headers.append('x-custom-header', 'some value')
506
+
507
+ // transform the URL by returning a modified URL
508
+ return url + '?param=value'
509
+ },
510
+ ],
511
+ response: [
512
+ (response, url, options) => {
513
+ if (response.ok) return
514
+ console.log('request was not successful', { url, options })
515
+
516
+ // You can transform the response by returning different `Response` object or even make a completely new HTTP request.
517
+ // The subsequent response interceptors will receive the returned response
518
+ return fetch('[DUMMYJSON-DOT-COM]/products/1') // promise will be resolved automatically
519
+ },
520
+ ],
521
+ result: [
522
+ (result, url, options) => {
523
+ const productId = Number(new URL(url).pathname.split('/products/')[1])
524
+ if (options.method === 'get' && !Number.isNaN(productId)) {
525
+ result.title ??= 'Unknown title'
526
+ }
527
+ return result
528
+ },
529
+ ],
533
530
  }
534
531
  fetch
535
- .get('[DUMMYJSON-DOT-COM]/products/1', { interceptors })
536
- .then(product => console.log({ product }))
532
+ .get('[DUMMYJSON-DOT-COM]/products/1', { interceptors })
533
+ .then(product => console.log({ product }))
537
534
  ```
538
535
 
539
536
  **Example: Add global request and error interceptors**
@@ -544,14 +541,14 @@ import fetch from '@superutils/fetch'
544
541
  const { interceptors } = fetch.defaults
545
542
 
546
543
  interceptors.request.push((url, options) => {
547
- // a headers to all requests make by the application
548
- // add headers to all requests made by the application
549
- options.headers.append('x-auth', 'token')
544
+ // a headers to all requests make by the application
545
+ // add headers to all requests made by the application
546
+ options.headers.append('x-auth', 'token')
550
547
  })
551
548
 
552
549
  interceptors.error.push((err, url, options) => {
553
- // log whenever a request fails
554
- console.log('Error interceptor', err)
550
+ // log whenever a request fails
551
+ console.log('Error interceptor', err)
555
552
  })
556
553
 
557
554
  // Each time a requst is made using @superutils/fetch, the above interceptors will be executed when appropriate
@@ -568,19 +565,12 @@ The `retry` option provides a robust mechanism to automatically re-attempt faile
568
565
  import fetch from '@superutils/fetch'
569
566
 
570
567
  fetch
571
- .get('[DUMMYJSON-DOT-COM]/products/1', {
572
- retry: 3, // If request fails, retry up to three more times
573
- // Additionally, you can control the retry strategy by using a function
574
- retryIf: async (response, retryCount, error) => {
575
- if (!!error) return true
576
-
577
- // Mke sure to clone the response if result stream must be consumed here.
578
- // Cloning is required to avoid "body stream already read" error.
579
- const result = await response.clone().json()
580
- return result !== 'expected value'
581
- },
582
- })
583
- .then(console.log)
568
+ .get('[DUMMYJSON-DOT-COM]/products/1', {
569
+ retry: 3, // If request fails, retry up to three more times
570
+ // Retry on rate limits (429) or transient server errors (5xx).
571
+ retryIf: r => r.status === 429 || r.status >= 500,
572
+ })
573
+ .then(console.log)
584
574
  ```
585
575
 
586
576
  <div id="create-client"></div>
@@ -596,42 +586,42 @@ import { createClient } from '@superutils/fetch'
596
586
 
597
587
  // Create a "GET" client with default headers and a 5-second timeout
598
588
  const apiClient = createClient(
599
- {
600
- // fixed options (cannot be overridden)
601
- method: 'get',
602
- },
603
- {
604
- // common options (can be overridden)
605
- headers: {
606
- Authorization: 'Bearer my-secret-token',
607
- 'Content-Type': 'application/json',
608
- },
609
- timeout: 5000,
610
- },
611
- {
612
- // defer options (can be overridden)
613
- delay: 300,
614
- retry: 2, // If request fails, retry up to two more times
615
- },
589
+ {
590
+ // fixed options (cannot be overridden)
591
+ method: 'get',
592
+ },
593
+ {
594
+ // common options (can be overridden)
595
+ headers: {
596
+ Authorization: 'Bearer my-secret-token',
597
+ 'Content-Type': 'application/json',
598
+ },
599
+ timeout: 5000,
600
+ },
601
+ {
602
+ // defer options (can be overridden)
603
+ delay: 300,
604
+ retry: 2, // If request fails, retry up to two more times
605
+ },
616
606
  )
617
607
 
618
608
  // Use it just like the standard fetch
619
609
  apiClient('[DUMMYJSON-DOT-COM]/products/1', {
620
- // The 'method' property cannot be overridden as it is used in the fixed options when creating the client.
621
- // In TypeScript, the compiler will not allow this property.
622
- // In Javascript, it will simply be ignored.
623
- // method: 'post',
624
- timeout: 3000, // The 'timeout' property can be overridden
610
+ // The 'method' property cannot be overridden as it is used in the fixed options when creating the client.
611
+ // In TypeScript, the compiler will not allow this property.
612
+ // In Javascript, it will simply be ignored.
613
+ // method: 'post',
614
+ timeout: 3000, // The 'timeout' property can be overridden
625
615
  }).then(console.log, console.warn)
626
616
 
627
617
  // create a deferred client using "apiClient"
628
618
  const deferredClient = apiClient.deferred(
629
- { retry: 0 }, // disable retrying by overriding the `retry` defer option
630
- '[DUMMYJSON-DOT-COM]/products/1',
631
- { timeout: 3000 },
619
+ { retry: 0 }, // disable retrying by overriding the `retry` defer option
620
+ '[DUMMYJSON-DOT-COM]/products/1',
621
+ { timeout: 3000 },
632
622
  )
633
623
  deferredClient({ timeout: 10000 }) // timeout is overridden by individual request
634
- .then(console.log, console.warn)
624
+ .then(console.log, console.warn)
635
625
  ```
636
626
 
637
627
  <div id="create-post-client"></div>
@@ -647,57 +637,72 @@ import { createPostClient } from '@superutils/fetch'
647
637
 
648
638
  // Create a POST client with 10-second as the default timeout
649
639
  const postClient = createPostClient(
650
- {
651
- headers: { 'content-type': 'application/json' },
652
- },
653
- {
654
- method: 'post',
655
- timeout: 10000,
656
- },
640
+ {
641
+ headers: { 'content-type': 'application/json' },
642
+ },
643
+ {
644
+ method: 'post',
645
+ timeout: 10000,
646
+ },
657
647
  )
658
648
 
659
649
  // Invoking `postClient()` automatically applies the pre-configured options
660
650
  postClient(
661
- '[DUMMYJSON-DOT-COM]/products/add',
662
- { title: 'New Product' }, // data/body
663
- {}, // other options
651
+ '[DUMMYJSON-DOT-COM]/products/add',
652
+ { title: 'New Product' }, // data/body
653
+ {}, // other options
664
654
  ).then(result => console.log('Product created:', result))
665
655
 
666
656
  // create a deferred client using "postClient"
667
657
  const deferredPatchClient = postClient.deferred(
668
- {
669
- delay: 300,
670
- // prints only successful results
671
- onResult: result =>
672
- console.log('Product updated using deferred funciton:', result),
673
- },
674
- '[DUMMYJSON-DOT-COM]/products/add',
675
- undefined, // data to be provided later
676
- {
677
- method: 'patch', // default method for deferredPatchClient
678
- timeout: 3000,
679
- },
658
+ {
659
+ delay: 300,
660
+ // prints only successful results
661
+ onResult: result =>
662
+ console.log('Product updated using deferred function:', result),
663
+ },
664
+ '[DUMMYJSON-DOT-COM]/products/add',
665
+ undefined, // data to be provided later
666
+ {
667
+ method: 'patch', // default method for deferredPatchClient
668
+ timeout: 3000,
669
+ },
680
670
  )
681
671
  deferredPatchClient({ title: 'New title 1' }) // ignored by debounce
682
672
  deferredPatchClient({ title: 'New title 2' }) // executed
683
673
  ```
684
674
 
685
- <div id="fetchFunc"></div>
675
+ <div id="fetch-func"></div>
686
676
 
687
- ### `fetchFunc`: Using Third-Party Libraries (e.g., Axios)
677
+ ### `fetchFunc`: Using with third-party libraries (e.g., Axios)
688
678
 
689
679
  The `fetchFunc` option allows you to replace the default request engine. This enables the use of third-party libraries like `axios` while still leveraging `@superutils/fetch` features such as [retries](#retry), [timeouts](#timeout), [debouncing/throttling](#fetch-deferred), and [interceptors](#interceptors).
690
680
 
691
681
  ```typescript
692
- import fetch, { type FetchFunc } from '@superutils/fetch'
682
+ import fetch, {
683
+ FetchCustomOptions,
684
+ FetchFunc,
685
+ FetchOptions,
686
+ } from '@superutils/fetch'
693
687
  import axios from 'axios'
694
688
 
689
+ type Product = {
690
+ id: number
691
+ title: string
692
+ }
695
693
  fetch
696
- .get('[DUMMYJSON-DOT-COM]/products/1', {
697
- // Note: The custom function must resolve with a standard Response object
698
- fetchFunc: axios as FetchFunc,
699
- retry: 3,
700
- // ...additional options
701
- })
702
- .then(console.log)
694
+ .get<{ data: Product }>('[DUMMYJSON-DOT-COM]/products/1', {
695
+ /**
696
+ * Note: Ensure request options are compatible with the third-party
697
+ * engine's configuration schema.
698
+ *
699
+ * Check {@link FetchOptions} (includes `FetchCustomOptions`).
700
+ */
701
+ fetchFunc: axios as FetchFunc,
702
+
703
+ // if request fails retry maximus 3 more times
704
+ retry: 3,
705
+ // ...additional options
706
+ })
707
+ .then(({ data }) => console.log({ product: data }))
703
708
  ```