11# @gkoos/ffetch
22
3- ** A TypeScript-first fetch wrapper that adds production-grade resilience in <4 kB .**
3+ ** A production-ready TypeScript-first drop-in replacement for native fetch .**
44
55- ** Timeouts** – per-request or global
66- ** Retries** – exponential back-off + jitter
@@ -24,13 +24,15 @@ import createClient from '@gkoos/ffetch'
2424const f = createClient ({
2525 timeout: 5000 ,
2626 retries: 3 ,
27- retryDelay : (n ) => 2 ** n * 100 + Math .random () * 100 ,
27+ retryDelay : ({ attempt } ) => 2 ** attempt * 100 + Math .random () * 100 ,
2828})
2929
3030const res = await f (' https://api.example.com/v1/users' )
3131const data = await res .json ()
3232```
3333
34+ ---
35+
3436## API
3537
3638createClient(options?)
@@ -39,7 +41,7 @@ createClient(options?)
3941| ------------ | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
4042| ` timeout ` | ` number ` (ms) | whole-request timeout |
4143| ` retries ` | ` number ` (0) | max retry attempts |
42- | ` retryDelay ` | ` number \| fn ` (exponential backoff + jitter) | delay between retries |
44+ | ` retryDelay ` | ` number \| (ctx: { attempt, request, response, error }) => number ` (exponential backoff + jitter) | delay between retries |
4345| ` circuit ` | ` { threshold, reset } ` | circuit-breaker rules |
4446| ` hooks ` | ` { before, after, onError, onRetry, onTimeout, onAbort, onCircuitOpen, onComplete, transformRequest, transformResponse } ` | lifecycle hooks/interceptors, transformers |
4547
@@ -52,6 +54,22 @@ type FFetch = (
5254) => Promise <Response >
5355` ` `
5456
57+ ### Defaults
58+
59+ | Option | Default Value / Logic |
60+ | ------------- | ---------------------------------------------------------------------------------------------------- |
61+ | ` timeout ` | ` 5000 ` ms (5 seconds) |
62+ | ` retries ` | ` 0 ` (no retries) |
63+ | ` retryDelay ` | Exponential backoff + jitter: <br> ` ({ attempt }) => 2 ** attempt * 200 + Math .random () * 100 ` |
64+ | ` shouldRetry ` | Retries on network errors, HTTP 5xx, or 429. <br>Does not retry on 4xx (except 429) or abort/timeout |
65+ | ` circuit ` | ` undefined ` (circuit breaker disabled by default) |
66+ | ` hooks ` | ` {}` (no hooks by default) |
67+
68+ **Note:**
69+
70+ - The first retry attempt uses ` attempt = 2 ` (i.e., the first call is attempt 1, first retry is 2).
71+ - ` shouldRetry ` default logic: retries on network errors, HTTP 5xx, or 429; does not retry on 4xx (except 429), abort, or timeout errors.
72+
5573## Advanced
5674
5775### Per-request overrides
@@ -69,7 +87,7 @@ await f('https://api.example.com/v1/users', {
6987
7088// Use a custom retry delay function for a single request
7189await f (' https://api.example.com/v1/data' , {
72- retryDelay : (attempt ) => 100 * attempt , // linear backoff for this request
90+ retryDelay : ({ attempt } ) => 100 * attempt , // linear backoff for this request
7391})
7492
7593// Override hooks for a single request
@@ -80,6 +98,33 @@ await f('https://api.example.com/v1/metrics', {
8098})
8199```
82100
101+ ### Retry/Backoff and Retry Policy
102+
103+ #### retryDelay
104+
105+ You can provide a function for ` retryDelay ` that receives a context object:
106+
107+ ``` typescript
108+ retryDelay : ({ attempt , request , response , error }) => {
109+ // attempt: number (starts at 2 for first retry)
110+ // request: Request
111+ // response: Response | undefined
112+ // error: unknown
113+ return 100 * attempt
114+ }
115+ ```
116+
117+ #### shouldRetry
118+
119+ You can provide a function for ` shouldRetry ` that receives the same context object:
120+
121+ ``` typescript
122+ shouldRetry : ({ attempt , request , response , error }) => {
123+ // Retry only on 503
124+ return response ?.status === 503
125+ }
126+ ```
127+
83128### Custom Error Types
84129
85130` ffetch ` throws custom error classes for robust error handling. You can catch and handle these errors as needed:
@@ -209,7 +254,7 @@ These hooks allow you to inject authentication, modify request/response bodies,
209254
210255---
211256
212- ### Note on Timeout vs Abort Errors
257+ ## Note on Timeout vs Abort Errors
213258
214259In most environments, ` ffetch ` will throw a ` TimeoutError ` if a request times out, and an ` AbortError ` if the user aborts the request. However, due to differences in how abort signals are handled in Node.js, browsers, and CI environments, a timeout may sometimes surface as an ` AbortError ` instead of a ` TimeoutError ` (especially in automated test environments).
215260
@@ -229,6 +274,11 @@ try {
229274
230275This is a pragmatic workaround for cross-environment compatibility.
231276
232- ### License
277+ ## Planned Features
278+
279+ - Middleware support
280+ - Built-in caching
281+
282+ ## License
233283
234284MIT © 2025 gkoos
0 commit comments