Overview
The withResilience function is the main entry point for adding resilience patterns to your functions. It wraps any function with retry logic, timeouts, backoff strategies, circuit breakers, and lifecycle hooks.
Function Signature
function withResilience < Fn extends ( ... args : any []) => any >(
fn : Fn ,
config ?: ResilienceConfig
) : ( ... args : Parameters < Fn >) => Promise < Awaited < ReturnType < Fn >>>
Parameters
fn
Fn extends (...args: any[]) => any
required
The function to wrap with resilience behavior. Can be synchronous or asynchronous.
config
ResilienceConfig
default: "{}"
Configuration object for resilience behavior. See Configuration for all available options. Optional name for the function (used in hooks and error messages). Defaults to fn.name or “anonymous”.
Number of retry attempts after initial failure.
Maximum execution time in milliseconds before timing out.
Backoff strategy for retries (fixed or exponential).
retryOn
(err: unknown) => boolean
Predicate function to determine if an error should trigger a retry. Defaults to always retry.
Circuit breaker configuration to prevent cascading failures.
Lifecycle hooks for observability and metrics. See Hooks . Enable automatic abort signal management for cancellation support.
Return Value
wrappedFunction
(...args: Parameters<Fn>) => Promise<Awaited<ReturnType<Fn>>>
A wrapped version of the original function that:
Returns a Promise (even if the original function was synchronous)
Maintains the same parameter types as the original function
Returns the same type as the original function (unwrapped from Promise if needed)
Implements all configured resilience patterns
Examples
Basic Retry
import { withResilience } from '@oldwhisper/resilience' ;
const fetchUser = async ( id : string ) => {
const response = await fetch ( `/api/users/ ${ id } ` );
return response . json ();
};
const resilientFetchUser = withResilience ( fetchUser , {
retries: 3 ,
backoff: {
type: 'exponential' ,
baseDelayMs: 100 ,
maxDelayMs: 5000 ,
jitter: true
}
});
// Use it like the original function
const user = await resilientFetchUser ( '123' );
With Timeout and Circuit Breaker
const callExternalAPI = withResilience (
async ( endpoint : string ) => {
return await fetch ( endpoint ). then ( r => r . json ());
},
{
name: 'external-api-call' ,
retries: 2 ,
timeoutMs: 3000 ,
circuitBreaker: {
failureThreshold: 5 ,
resetTimeoutMs: 60000 // 1 minute
},
retryOn : ( err ) => {
// Only retry on network errors, not 4xx responses
return err instanceof TypeError ;
}
}
);
With Hooks for Metrics
const processData = withResilience (
async ( data : any []) => {
// Processing logic
return data . map ( item => transform ( item ));
},
{
name: 'data-processor' ,
retries: 3 ,
hooks: {
onAttempt : ({ name , attempt }) => {
console . log ( `[ ${ name } ] Attempt ${ attempt } ` );
},
onSuccess : ({ name , attempt , timeMs }) => {
metrics . recordSuccess ( name , timeMs , attempt );
},
onFailure : ({ name , attempt , timeMs , error }) => {
metrics . recordFailure ( name , timeMs , attempt , error );
},
onRetry : ({ name , attempt , delayMs , error }) => {
console . warn ( `[ ${ name } ] Retrying after ${ delayMs } ms due to:` , error );
}
}
}
);
With Abort Signal Support
const longRunningTask = withResilience (
async ( taskId : string ) => {
// This function can use resilientFetch and sleep
// which will automatically be cancelled on timeout
await sleep ( 1000 );
const result = await resilientFetch ( `/api/tasks/ ${ taskId } ` );
await sleep ( 1000 );
return result . json ();
},
{
name: 'long-task' ,
retries: 2 ,
timeoutMs: 5000 ,
useAbortSignal: true // Enable automatic cancellation
}
);
Error Handling
The wrapped function will throw errors in the following cases:
TimeoutError : When execution exceeds timeoutMs
CircuitOpenError : When the circuit breaker is open and blocks execution
Original Error : When retries are exhausted or retryOn returns false
try {
await resilientFunction ();
} catch ( error ) {
if ( error . message === 'TimeoutError' ) {
// Handle timeout
} else if ( error . message . startsWith ( 'CircuitOpenError' )) {
// Handle circuit breaker
} else {
// Handle original error
}
}
Implementation Details
From src/index.ts:113-168, the function:
Creates a circuit breaker if configured
Wraps the function with retry logic (attempts = retries + 1)
For each attempt:
Calls onAttempt hook
Checks if circuit breaker allows execution
Creates an AbortController if useAbortSignal is enabled
Executes the function with timeout wrapper
Calls onSuccess hook on success
Calls onFailure hook on failure
Determines if retry should occur based on retryOn
Waits for backoff delay before next attempt