@lookworld4/resilient-fetch
v1.0.0
Published
Wraps native fetch with automatic retries using exponential backoff and jitter—smooth UX through brief outages and flaky networks.
Maintainers
Readme
@lookworld4/resilient-fetch
Small utility around the native fetch API that retries failed requests with exponential backoff and jitter. When the network flickers or a server returns a transient error, the client can recover without exposing a brittle “failed to load” experience.
- Self-healing by default: automatic delays between attempts spread load and avoid thundering herds (full jitter by default).
- Abort-aware: respects
AbortSignaloninit, including waits between retries. - Safe defaults: by default only GET, HEAD, and OPTIONS retry on retryable HTTP status codes (408, 429, 500, 502–504). POST/PUT/etc. still retry network-layer failures only, to reduce accidental double-submits.
- Zero runtime dependencies, zero devDependencies (hand-written JS + types in
dist/).
Requires Node ≥ 18 (global fetch) or any environment that provides globalThis.fetch (modern browsers and workers).
Install
npm install @lookworld4/resilient-fetchUsage
import { resilientFetch } from '@lookworld4/resilient-fetch';
const response = await resilientFetch('https://api.example.com/v1/ping', {}, {
retries: 3,
initialDelayMs: 100,
jitter: 'full',
});Custom retry rules
Use shouldRetry, or derive a reusable client:
import { createResilientFetch, resilientFetch } from '@lookworld4/resilient-fetch';
const apiFetch = createResilientFetch({ retries: 5, backoffMultiplier: 2 });
await apiFetch('https://api.example.com/rpc', {
method: 'POST',
body: '{"id":1}',
headers: { 'content-type': 'application/json' },
}, {
// override per call
shouldRetry: ({ error, response, attempt, method }) => {
if (error) return true;
if (!response) return false;
if (response.status === 503 && method === 'POST') return attempt < 1;
return false;
},
});Responses that include Retry-After (seconds or HTTP date) extend the backoff when that implies a longer wait.
Retry options
| Field | Default | Meaning |
|--------|---------|---------|
| retries | 3 | Retry attempts after the first try (≤ retries + 1 attempts). |
| initialDelayMs | 100 | Base backoff (ms); grows exponentially. |
| maxDelayMs | 30000 | Cap between attempts. |
| backoffMultiplier | 2 | Multiply delay each attempt (after jitter cap). |
| jitter | 'full' | 'full', 'equal', or 'none' (see AWS-style full jitter patterns). |
| shouldRetry | built-in safe policy | Receives { error?, response?, attempt, method }. |
| fetch | globalThis.fetch | Inject for testing or exotic runtimes. |
Replayable bodies
fetch can only consume a streamed body once. For requests that retry after a parsed response failure, prefer string/URLSearchParams/Blob bodies rather than unreadable streams.
License
MIT
