-
Notifications
You must be signed in to change notification settings - Fork 0
services retry
io.retry (attached by default) retries failed requests with exponential backoff, but only when it is safe to do so: idempotent verbs retry automatically, while a bare POST does not.
import io from 'double-meh';
const data = await io.get('https://example.com/r', null, {retry: 3});
// a 5xx / 429 / network error is retried up to 3 times, then the last result standsRetries are off unless the request passes retry: true for the default budget (io.retry.retries, 2), a number for that many retries, or a config object (see below). What counts as a failure is a network error, any 5xx, or 429.
-
retry: true— retry with the default budget (io.retry.retries). -
retry: n— up tonretries. -
retry: {...}— a config object:-
retries— maximum retry attempts (defaultio.retry.retries, 2). -
initDelay— first backoff delay in ms (defaultio.retry.initDelay, 100). UseinitDelay: 0for immediate retries. -
force—trueoverrides the verb-safety gate below. -
nextDelay(delay, attempt, options)— per-request delay policy (defaultio.retry.nextDelay). -
continueRetries(response, attempt, options)— a predicate deciding whether to keep going after a response.
-
await io.get('https://example.com/r', null, {retry: {retries: 3, initDelay: 200}});GET, HEAD, OPTIONS, PUT, and DELETE are idempotent and retry automatically. PATCH retries only with ifMatch or as: 'merge-patch'. A bare POST is not retried; it must carry an idempotencyKey or If-None-Match: * (ifNoneMatch: '*') to unlock retry. Pass retry: {force: true} to retry regardless, or retry: false (or no retry at all) to disable it.
// bare POST: not retried even with a retry budget
await io.post('https://example.com/p', {a: 1}, {retry: 3, ignoreBadStatus: true});
// POST with an idempotency key: retried, same key reused across attempts
await io.post('https://example.com/p', {a: 1}, {retry: 1, idempotencyKey: true});
// force: retried regardless of the verb
await io.post('https://example.com/p', {a: 1}, {retry: {retries: 2, force: true}});When a failed response carries a Retry-After header (delta-seconds or an HTTP date), that delay is used for the next attempt instead of the backoff timer, clamped to io.retry.maxDelay (default 30 s).
await io.get('https://example.com/rate-limited', null, {retry: 2});
// a 429 with `Retry-After: 5` waits ~5s before retryingWhen continueRetries is present it replaces the built-in status check: after each response the predicate decides whether to try again (a thrown network error still consumes the retries budget). With retries: 0 the loop is driven solely by the predicate — the polling mode.
// poll until the job leaves 202 Accepted
const job = await io.get('https://example.com/job/7', null, {
retry: {retries: 0, continueRetries: response => response.status === 202}
});If a DELETE succeeds on the server but its response is lost and a retry then returns 404, the resource is already gone — so a 404 on any attempt after the first is rewritten to 204 No Content.
const env = await io.full.delete('https://example.com/d', null, {retry: 3});
env.status; // 204 when a retried DELETE hits 404io.retry.nextDelay(delay, attempt, options) computes the next backoff from the current one; the default doubles it and caps at 5s. Replace it to change the curve instance-wide, or pass nextDelay in the retry config for one request.
io.retry.nextDelay = delay => Math.min(delay * 3, 10_000); // instance-wide
await io.get(url, null, {retry: {retries: 3, nextDelay: () => 1000}}); // per requestAn aborted request is never retried — the abort propagates immediately. Requests with a streaming (ReadableStream) body are not retried either, since the body cannot be replayed.
Each retry attempt emits a 'retry' event with {attempt, response, error} — see Events.
The main entry attaches the retry service for you. io.retry.detach() removes it; io.retry.attach() reinstalls it. Both maintain io.retry.isActive and return io. Instance-wide defaults live on io.retry: {retries: 2, initDelay: 100, maxDelay: 30000, nextDelay}.
io.retry.detach();
io.retry.attach();Guides
Concepts
Services
Modules
- fetch transport
- keys & URLs
- helpers
- records
- sse
- storage backends
- compression encoders
- Service Worker integration
Cookbook