Async retry with exponential backoff, circuit breaker, and abort signals
npm install @philiprehberger/retry-kitAsync retry with exponential backoff, circuit breaker, and abort signals
npm install @philiprehberger/retry-kit
import { retry } from '@philiprehberger/retry-kit';
const data = await retry(() => fetch('/api/data').then(r => r.json()), {
maxAttempts: 3,
backoff: 'exponential',
initialDelay: 1000,
});
const result = await retry(() => fetch('/api/data'), {
maxAttempts: 5,
backoff: 'exponential', // 'exponential' | 'linear' | 'fixed'
backoffMultiplier: 2, // base for exponential backoff (default: 2)
initialDelay: 1000,
maxDelay: 30000,
jitter: true,
timeout: 5000, // per-attempt timeout
totalTimeout: 30000, // total timeout across all attempts
signal: AbortSignal.timeout(60000),
retryOn: (error) => error.status >= 500,
onRetry: (error, attempt) => console.log(`Retry ${attempt}...`),
});
import { retry, presets } from '@philiprehberger/retry-kit';
await retry(fn, presets.networkRequest);
await retry(fn, presets.databaseQuery);
await retry(fn, presets.aggressive);
await retry(fn, presets.gentle);
import { retryUntil } from '@philiprehberger/retry-kit';
// Poll a job endpoint until it reports completion
const job = await retryUntil(
() => fetch(`/api/jobs/${id}`).then((r) => r.json()),
(j) => j.status === 'done',
{ maxAttempts: 20, initialDelay: 500, backoff: 'exponential', maxDelay: 5000 },
);
import { withCircuitBreaker } from '@philiprehberger/retry-kit';
const resilientFetch = withCircuitBreaker(fetch, {
failureThreshold: 5, // open after 5 failures
resetTimeout: 30000, // try again after 30s
halfOpenSuccessThreshold: 2, // require 2 successes in half-open before closing
onStateChange: (from, to) => console.log(`Circuit: ${from} → ${to}`),
onCircuitOpen: (failures) => console.warn(`Circuit opened after ${failures} failures`),
});
// Inspect current circuit state
console.log(resilientFetch.getState()); // 'closed' | 'open' | 'half-open'
try {
await resilientFetch('/api/data');
} catch (error) {
if (error.name === 'CircuitOpenError') {
// Circuit is open, fail fast
}
}
| Export | Type | Description |
|---|---|---|
retry(fn, options?) | Function | Retry an async function with configurable backoff and abort support |
retryUntil(fn, predicate, options?) | Function | Poll until predicate(result) is true; reuses retry's backoff/jitter/signal |
withCircuitBreaker(fn, options?) | Function | Wrap a function with circuit breaker protection; returns callable with .getState() |
presets | Object | Built-in retry option presets: aggressive, gentle, networkRequest, databaseQuery |
RetryError | Class | Thrown when all retry attempts are exhausted |
CircuitOpenError | Class | Thrown when circuit breaker is open |
RetryOptions | Type | Options: maxAttempts, backoff, initialDelay, maxDelay, jitter, timeout, totalTimeout, signal, retryOn, onRetry, onSuccess, onFailure |
BackoffStrategy | Type | 'exponential' | 'linear' | 'fixed' |
CircuitBreakerOptions | Type | Options: failureThreshold, resetTimeout, halfOpenSuccessThreshold, onStateChange, onCircuitOpen |
CircuitState | Type | 'closed' | 'open' | 'half-open' |
npm install
npm run build
npm test
If you find this project useful: