Skip to content

Introduction

resilience-pipeline wraps calls to things that fail: HTTP APIs, databases, queues, LLM providers. You describe how to react to failure once, as a pipeline of strategies, and run any call through it.

import {
class ResiliencePipelineBuilder<T = any, E = never, C extends string = never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
} from 'resilience-pipeline';
const
const payments: ResiliencePipeline<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>>
payments
= new
new ResiliencePipelineBuilder<any, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<any, never, never>

Polly-style builder with a shortcut for every strategy: addRetry(), addTimeout(), addCircuitBreaker(), … Convenient, but it bundles every strategy. For the smallest browser bundle use PipelineBuilder with retryStrategy(), timeoutStrategy(), … instead; the two are interchangeable.

The type parameters track which errors the pipeline can raise, for pipeline.settle() + Outcome.match().

ResiliencePipelineBuilder
({
ResiliencePipelineBuilderOptions.name?: string | undefined

Labels telemetry events.

name
: 'payments' })
.
ResiliencePipelineBuilder<any, never, never>.addRetry(options?: RetryOptions<any>): ResiliencePipelineBuilder<any, never, never>
addRetry
({
RetryOptions<any>.maxRetryAttempts?: number | undefined

Retries in addition to the original call. Default 3. Infinity retries until aborted.

maxRetryAttempts
: 3,
RetryOptions<any>.backoffType?: BackoffType | undefined

Default 'constant'.

backoffType
: 'exponential',
RetryOptions<any>.useJitter?: Jitter | undefined
useJitter
: true })
.
ResiliencePipelineBuilder<any, never, never>.addCircuitBreaker(options?: CircuitBreakerOptions<any>): Next<any, never, never, BrokenCircuitError>
addCircuitBreaker
({
CircuitBreakerOptions<any>.failureRatio?: number | undefined

Failure ratio in the window that breaks the circuit. Default 0.1.

failureRatio
: 0.5,
CircuitBreakerOptions<any>.minimumThroughput?: number | undefined

Minimum recorded calls in the window before the ratio is considered. Default 100.

minimumThroughput
: 20 })
.
ResiliencePipelineBuilder<any, BrokenCircuitError, never>.addTimeout(options: Duration | TimeoutOptions): Next<any, BrokenCircuitError, never, TimeoutRejectedError>

addTimeout(5000), addTimeout('5s') or addTimeout({ timeout: '5s', onTimeout }).

addTimeout
('2s')
.
ResiliencePipelineBuilder<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>, never>.build(): ResiliencePipeline<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>>
build
();
const
const receipt: {
id: string;
}
receipt
= await
const payments: ResiliencePipeline<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>>
payments
.
ResiliencePipeline<any, WithErrors<BrokenCircuitError, never, TimeoutRejectedError>>.execute<{
id: string;
}>(callback: (context: ResilienceContext) => MaybePromise<{
id: string;
}>, options?: ExecuteOptions): Promise<{
id: string;
}>

Runs the callback through the pipeline; throws the final error.

execute
(({
signal: AbortSignal

Aborted when the caller cancels, or when a strategy (timeout, hedging) gives up on this execution.

signal
}) =>
function chargeCard(amount: number, signal: AbortSignal): Promise<{
id: string;
}>
chargeCard
(4200,
signal: AbortSignal

Aborted when the caller cancels, or when a strategy (timeout, hedging) gives up on this execution.

signal
));

Each attempt gets a 2-second timeout and passes through the circuit breaker. Transient failures are retried with exponential backoff; once the breaker opens, calls fail fast instead of piling onto a dead service.

Failure mode Strategies
Transient errors Retry with backoff, jitter and a shared retry budget
A dependency that is down Circuit breaker, fallback, stale fallback
Slow calls Timeout, hedging, deadline propagation
Overload Concurrency limiter, adaptive concurrency, rate limiters
Duplicate work Cache and coalesce, batcher, lock
Proving it works Chaos, FakeTimeProvider, telemetry

The API follows Polly v8: ResiliencePipelineBuilder, addRetry, shouldHandle, PredicateBuilder, the same option and event names. It is an independent TypeScript implementation with no runtime dependencies. It runs on Node.js ≥ 20.16, Bun, Deno, Cloudflare Workers and browsers.

  • You call something over the network and want retries, timeouts and a breaker that agree with each other.
  • You want errors you can handle exhaustively: settle() and Outcome.match() make a forgotten “circuit open” case a compile error.
  • You need one tool for both client side (fetch, axios, database calls) and server side (load shedding, per-client rate limits, request deadlines).

If all you need is a single retry loop, the retry() one-liner does that in 4.1 KB without building a pipeline.