Skip to main content

Import

import { RetryPolicy } from "bytekit/retry-policy";

What it does

RetryPolicy wraps any async function with automatic retries using exponential backoff and jitter. When all attempts are exhausted, the last error is thrown. A configurable shouldRetry predicate controls which errors trigger retries.
ApiClient creates an internal RetryPolicy from the retryPolicy config option. Use this class directly when you need retries outside of HTTP requests.

Constructor

const policy = new RetryPolicy(config?: RetryConfig);

RetryConfig

PropertyTypeDefaultDescription
maxAttemptsnumber3Total number of attempts (including the first).
initialDelayMsnumber100Base delay before the first retry (ms).
maxDelayMsnumber10000Maximum delay cap (ms).
backoffMultipliernumber2Multiplier applied to the delay after each attempt.
shouldRetry(error: Error, attempt: number) => booleanBuilt-inPredicate to decide whether a given error should be retried.
The built-in shouldRetry predicate retries on:
  • HTTP status 408 (timeout), 429 (rate limit), and 5xx (server errors)
  • Network-related error messages (timeout, network, ECONNREFUSED, ECONNRESET)

Methods

execute<T>(fn)

const result = await policy.execute(() => fetchData());
Runs fn up to maxAttempts times. Returns the result on the first success. Throws the last error if all attempts fail.
ParameterTypeDescription
fn() => Promise<T>The async function to execute with retries.

getConfig()

Returns the resolved configuration object, useful for debugging or logging.
console.log(policy.getConfig());
// { maxAttempts: 3, initialDelayMs: 100, maxDelayMs: 10000, ... }

How backoff works

The delay between retries follows this formula: delay = min( initialDelayMs × backoffMultiplier^(attempt - 1) + jitter, maxDelayMs ) Jitter is added automatically (up to 10% of the exponential delay) to prevent thundering-herd effects when many clients retry at the same time.

Examples

Basic retry

import { RetryPolicy } from "bytekit/retry-policy";

const policy = new RetryPolicy({
  maxAttempts: 3,
  initialDelayMs: 200,
  maxDelayMs: 5000,
  backoffMultiplier: 2,
});

const data = await policy.execute(() =>
  fetch("https://api.example.com/data").then((r) => r.json()),
);

Custom shouldRetry predicate

const policy = new RetryPolicy({
  maxAttempts: 5,
  shouldRetry: (error, attempt) => {
    // Only retry on network errors, not on 4xx
    const status = (error as { status?: number }).status;
    if (status && status >= 400 && status < 500) return false;
    return true;
  },
});

const result = await policy.execute(() => riskyOperation());

Wrapping a non-HTTP operation

const dbPolicy = new RetryPolicy({
  maxAttempts: 3,
  initialDelayMs: 500,
  shouldRetry: (error) => error.message.includes("ECONNREFUSED"),
});

const rows = await dbPolicy.execute(() => db.query("SELECT * FROM users"));
RetryPolicy does not deduplicate in-flight calls. If you call execute concurrently with the same function, each call retries independently. Combine with RequestDeduplicator if you need deduplication.