Skip to content
Buy me a coffee

Fallback

Fallback turns a failure into a usable answer: a cached value, a default, a degraded response. It runs after the strategies added behind it have given up, so it is usually the first strategy you add (the outermost).

Use it when a degraded answer is better than an error: recommendations, prices from cache, a feature flag’s default. Don’t use it to hide failures the caller must know about, such as a payment that didn’t go through.

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 '@ylcnfrht/resilience';
const
const pipeline: ResiliencePipeline<Record<string, number>, never>
pipeline
= new
new ResiliencePipelineBuilder<Record<string, number>, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<Record<string, number>, 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
<
type Record<K extends keyof any, T> = { [P in K]: T; }

Construct a type with a set of properties K of type T

Record
<string, number>>()
.
ResiliencePipelineBuilder<Record<string, number>, never, never>.addFallback(options: FallbackOptions<Record<string, number>> & {
shouldHandle?: undefined;
}): ResiliencePipelineBuilder<Record<string, number>, never, string> (+1 overload)

Without shouldHandle it catches every failure of the strategies added after it, so their errors leave the error type.

addFallback
({
FallbackOptions<Record<string, number>>.fallbackAction?: ((args: OutcomeArgs<Record<string, number>>) => MaybePromise<Record<string, number>>) | undefined

Produces the substitute value; throwing here makes the execution fail with that error. Give either this or fallbackValue.

fallbackAction
: () =>
const lastKnownRates: Record<string, number>
lastKnownRates
}) // outermost: catches what's left
.
ResiliencePipelineBuilder<Record<string, number>, never, string>.addRetry(options?: RetryOptions<Record<string, number>>): ResiliencePipelineBuilder<Record<string, number>, never, string>
addRetry
({
RetryOptions<Record<string, number>>.maxRetryAttempts?: number | undefined

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

maxRetryAttempts
: 2 })
.
ResiliencePipelineBuilder<Record<string, number>, never, string>.addTimeout(options: Duration | TimeoutOptions): Next<Record<string, number>, never, string, TimeoutRejectedError>

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

addTimeout
('1s')
.
ResiliencePipelineBuilder<Record<string, number>, never, string>.build(): ResiliencePipeline<Record<string, number>, never>
build
();
const
const rates: Record<string, number>
rates
= await
const pipeline: ResiliencePipeline<Record<string, number>, never>
pipeline
.
ResiliencePipeline<Record<string, number>, never>.execute<Record<string, number>>(callback: (context: ResilienceContext) => MaybePromise<Record<string, number>>, options?: ExecuteOptions): Promise<Record<string, number>>

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 getRates(signal: AbortSignal): Promise<Record<string, number>>
getRates
(
signal: AbortSignal

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

signal
));
  1. The rest of the pipeline runs.
  2. If shouldHandle matches the outcome, onFallback runs, then fallbackAction(args) (or fallbackValue) becomes the result. With executeStream, a successful stream it replaces is closed.
  3. Otherwise the outcome passes through unchanged.

If fallbackAction throws or rejects, the execution fails with that error.

The fallback default is broader than retry’s: it also covers rejections, because “the circuit is open” is exactly when you want a substitute.

Outcome Default fallback Default retry / breaker
Errors: network, 5xx, timeouts, 408, 429 handled handled
Rejections: open circuit, full limiter (isRejection) handled not handled
Client errors: 4xx except 408/429 (isClientError) not handled not handled
Aborts: the caller went away, or an AbortError not handled not handled
Results (any value returned) not handled not handled

A 404 isn’t a failure to hide. A caller who cancelled doesn’t want an answer. To catch results or 4xx errors, pass your own shouldHandle; it replaces the default. See What counts as a failure.

fallbackAction fallbackValue
Computed Per failure, may be async Once, when you build the pipeline
Sees the failure Yes: { outcome, context } No
Use it for Cache lookups, a value that depends on the error or the request Constants: [], null, a default config

Give one of the two; with neither, build() throws RangeError. If both are set, fallbackAction wins.

const
const empty: ResiliencePipeline<Product[], never>
empty
= new
new ResiliencePipelineBuilder<Product[], never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<Product[], 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
<
type Product = {
id: string;
name: string;
}
Product
[]>().
ResiliencePipelineBuilder<Product[], never, never>.addFallback(options: FallbackOptions<Product[]> & {
shouldHandle?: undefined;
}): ResiliencePipelineBuilder<Product[], never, string> (+1 overload)

Without shouldHandle it catches every failure of the strategies added after it, so their errors leave the error type.

addFallback
({
FallbackOptions<Product[]>.fallbackValue?: Product[] | undefined

A fixed substitute value (cockatiel's fallback(policy, value)).

fallbackValue
: [] }).
ResiliencePipelineBuilder<Product[], never, string>.build(): ResiliencePipeline<Product[], never>
build
();
const
const cached: ResiliencePipeline<Product[], never>
cached
= new
new ResiliencePipelineBuilder<Product[], never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<Product[], 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
<
type Product = {
id: string;
name: string;
}
Product
[]>()
.
ResiliencePipelineBuilder<Product[], never, never>.addFallback(options: FallbackOptions<Product[]> & {
shouldHandle?: undefined;
}): ResiliencePipelineBuilder<Product[], never, string> (+1 overload)

Without shouldHandle it catches every failure of the strategies added after it, so their errors leave the error type.

addFallback
({
FallbackOptions<Product[]>.fallbackAction?: ((args: OutcomeArgs<Product[]>) => MaybePromise<Product[]>) | undefined

Produces the substitute value; throwing here makes the execution fail with that error. Give either this or fallbackValue.

fallbackAction
: async ({
outcome: Outcome<Product[], unknown>
outcome
,
context: ResilienceContext
context
}) => {
if (!
outcome: Outcome<Product[], unknown>
outcome
.
ok: boolean
ok
&&
function isBrokenCircuitError(error: unknown): error is BrokenCircuitError
isBrokenCircuitError
(
outcome: {
readonly ok: false;
readonly error: unknown;
}
outcome
.
error: unknown
error
))
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.warn(message?: any, ...optionalParams: any[]): void (+1 overload)

The console.warn() function is an alias for

error

.

@since ― v0.1.100

warn
('catalog circuit open');
return (await
const cache: {
get(key: string): Promise<Product[] | undefined>;
}
cache
.
function get(key: string): Promise<Product[] | undefined>
get
(
context: ResilienceContext
context
.
ResilienceContext.operationKey: string | undefined
operationKey
?? 'all')) ?? [];
},
FallbackOptions<Product[]>.onFallback?: ((args: OutcomeArgs<Product[]>) => MaybePromise<void>) | undefined
onFallback
: ({
outcome: Outcome<Product[], unknown>
outcome
}) =>
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.warn(message?: any, ...optionalParams: any[]): void (+1 overload)

The console.warn() function is an alias for

error

.

@since ― v0.1.100

warn
('serving fallback',
outcome: Outcome<Product[], unknown>
outcome
),
})
.
ResiliencePipelineBuilder<Product[], never, string>.addCircuitBreaker(options?: CircuitBreakerOptions<Product[]>): Next<Product[], never, string, BrokenCircuitError>
addCircuitBreaker
({
CircuitBreakerOptions<Product[]>.consecutiveFailures?: number | undefined

Break after this many failures in a row (a success resets the count). Simple and good for low traffic. When set without failureRatio, the ratio check is off and only this rule applies.

consecutiveFailures
: 5 })
.
ResiliencePipelineBuilder<Product[], never, string>.build(): ResiliencePipeline<Product[], never>
build
();
Option Type Default Description
name string 'Fallback' Strategy name in telemetry and getStrategy().
shouldHandle (args) => boolean (or a promise) or PredicateBuilder every failure except aborts and 4xx (408/429 included) Which outcomes get the fallback.
fallbackAction (args: { outcome, context }) => T (or a promise) none Produces the substitute. Throwing fails the execution with that error.
fallbackValue T none Fixed substitute.
onFallback (args: { outcome, context }) => void none Before the substitute is produced. May be async; it’s awaited.

addFallbackOn(codes, fallbackAction, options?) handles failures whose error.code is in codes and lets everything else through. options takes name, fallbackValue and onFallback.

const
const pipeline: ResiliencePipeline<number, TimeoutRejectedError>
pipeline
= new
new ResiliencePipelineBuilder<number, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<number, 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
<number>()
.
ResiliencePipelineBuilder<number, never, never>.addFallbackOn<"CIRCUIT_OPEN" | "CIRCUIT_ISOLATED">(codes: readonly ("CIRCUIT_OPEN" | "CIRCUIT_ISOLATED")[], fallbackAction: (args: OutcomeArgs<number>) => MaybePromise<number>, options?: Omit<FallbackOptions<number>, "fallbackAction" | "shouldHandle"> | undefined): ResiliencePipelineBuilder<number, never, "CIRCUIT_OPEN" | "CIRCUIT_ISOLATED">

Fallback for these error codes only; they leave the settle() error type of the strategies added after it.

addFallbackOn
(['CIRCUIT_OPEN', 'CIRCUIT_ISOLATED'], () =>
function cachedPrice(sku: string): number
cachedPrice
('sku-1'))
.
ResiliencePipelineBuilder<number, never, "CIRCUIT_OPEN" | "CIRCUIT_ISOLATED">.addCircuitBreaker(options?: CircuitBreakerOptions<number>): Next<number, never, "CIRCUIT_OPEN" | "CIRCUIT_ISOLATED", BrokenCircuitError>
addCircuitBreaker
({
CircuitBreakerOptions<number>.consecutiveFailures?: number | undefined

Break after this many failures in a row (a success resets the count). Simple and good for low traffic. When set without failureRatio, the ratio check is off and only this rule applies.

consecutiveFailures
: 5 })
.
ResiliencePipelineBuilder<number, never, "CIRCUIT_OPEN" | "CIRCUIT_ISOLATED">.addTimeout(options: Duration | TimeoutOptions): Next<number, never, "CIRCUIT_OPEN" | "CIRCUIT_ISOLATED", TimeoutRejectedError>

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

addTimeout
('2s')
.
ResiliencePipelineBuilder<number, TimeoutRejectedError, "CIRCUIT_OPEN" | "CIRCUIT_ISOLATED">.build(): ResiliencePipeline<number, TimeoutRejectedError>
build
();
const outcome = await
const pipeline: ResiliencePipeline<number, TimeoutRejectedError>
pipeline
.
ResiliencePipeline<number, TimeoutRejectedError>.settle<number, never>(callback: (context: ResilienceContext) => MaybePromise<number | Outcome<number, never>>, options?: ExecuteOptions): Promise<Outcome<number, TimeoutRejectedError | UnexpectedError>>

Like executeOutcome, but the error is typed: the errors this pipeline's strategies can raise (E), the errors your callback returns with Outcome.fromError(…) (E2), or UnexpectedError wrapping anything else (a thrown bug, a client error). Pair it with Outcome.match() for exhaustive handling:

const outcome = await pipeline.settle(async () => (user ? user : Outcome.fromError(new NotFound(id))));
Outcome.match(outcome, { ok: (u) => u, NOT_FOUND: () => null, TIMEOUT: () => cached, CIRCUIT_OPEN: () => cached, UNEXPECTED: (e) => { throw e } });

Returned errors count as failures for the strategies (they can be retried) unless your predicates say otherwise.

settle
(({
signal: AbortSignal

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

signal
}) =>
function getPrice(sku: string, signal: AbortSignal): Promise<number>
getPrice
('sku-1',
signal: AbortSignal

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

signal
));
const outcome: Outcome<number, TimeoutRejectedError | UnexpectedError>

The listed codes leave the pipeline’s typed error union; the timeout stays, so Outcome.match still needs a TIMEOUT branch. An error class leaves the union only when all its codes are listed: BrokenCircuitError can be CIRCUIT_OPEN or CIRCUIT_ISOLATED, so list both.

The equivalent for PipelineBuilder is fallbackOnStrategy(codes, fallbackAction, options?).

pipeline.settle() returns an outcome typed with every error the strategies can raise. Fallbacks remove the errors they handle, but only those of strategies added after them:

Strategy Error type afterwards
addFallback({ … }) without shouldHandle Every coded error of the strategies added after it is removed.
addFallback({ shouldHandle, … }) Unchanged: the compiler can’t know what your predicate catches.
addFallbackOn(codes, …) Errors whose codes are all in codes are removed.
addMapError(mapper) Every coded error of the strategies added after it is replaced by the mapper’s return type.
addStaleFallback({ … }) Unchanged: there may be no stale value to serve.

addMapError(mapper) doesn’t substitute a value. It turns every failure of the strategies after it, the callback’s included, into your own error. Use it at a service boundary so callers see PaymentUnavailable, not TimeoutRejectedError. Aborts pass through unchanged.

class
class PaymentUnavailable
PaymentUnavailable
extends
var Error: ErrorConstructor
Error
{
readonly
PaymentUnavailable.code: "PAYMENT_UNAVAILABLE"
code
= 'PAYMENT_UNAVAILABLE' as
type const = "PAYMENT_UNAVAILABLE"
const
;
}
const
const pipeline: ResiliencePipeline<string, PaymentUnavailable>
pipeline
= new
new ResiliencePipelineBuilder<string, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<string, 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
<string>()
.
ResiliencePipelineBuilder<string, never, never>.addMapError<PaymentUnavailable>(mapper: (error: unknown, context: ResilienceContext) => PaymentUnavailable): ResiliencePipelineBuilder<string, PaymentUnavailable, string>

Turns every failure of the strategies added after it into your own error type (theirs leave the error type).

addMapError
((
error: unknown
error
) => new
constructor PaymentUnavailable(message?: string, options?: ErrorOptions): PaymentUnavailable (+1 overload)
PaymentUnavailable
('payments are down', {
ErrorOptions.cause?: unknown
cause
:
error: unknown
error
}))
.
ResiliencePipelineBuilder<string, PaymentUnavailable, string>.addRetry(options?: RetryOptions<string>): ResiliencePipelineBuilder<string, PaymentUnavailable, string>
addRetry
()
.
ResiliencePipelineBuilder<string, PaymentUnavailable, string>.addTimeout(options: Duration | TimeoutOptions): Next<string, PaymentUnavailable, string, TimeoutRejectedError>

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

addTimeout
('2s')
.
ResiliencePipelineBuilder<string, PaymentUnavailable, string>.build(): ResiliencePipeline<string, PaymentUnavailable>
build
();
const
const outcome: Outcome<string, PaymentUnavailable | UnexpectedError>
outcome
= await
const pipeline: ResiliencePipeline<string, PaymentUnavailable>
pipeline
.
ResiliencePipeline<string, PaymentUnavailable>.settle<string, never>(callback: (context: ResilienceContext) => MaybePromise<string | Outcome<string, never>>, options?: ExecuteOptions): Promise<Outcome<string, PaymentUnavailable | UnexpectedError>>

Like executeOutcome, but the error is typed: the errors this pipeline's strategies can raise (E), the errors your callback returns with Outcome.fromError(…) (E2), or UnexpectedError wrapping anything else (a thrown bug, a client error). Pair it with Outcome.match() for exhaustive handling:

const outcome = await pipeline.settle(async () => (user ? user : Outcome.fromError(new NotFound(id))));
Outcome.match(outcome, { ok: (u) => u, NOT_FOUND: () => null, TIMEOUT: () => cached, CIRCUIT_OPEN: () => cached, UNEXPECTED: (e) => { throw e } });

Returned errors count as failures for the strategies (they can be retried) unless your predicates say otherwise.

settle
(({
signal: AbortSignal

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

signal
}) =>
function charge(signal: AbortSignal): Promise<string>
charge
(
signal: AbortSignal

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

signal
)); // error: PaymentUnavailable | UnexpectedError

mapper(error, context) runs synchronously. The PipelineBuilder form is mapErrorStrategy(mapper, name?) (default name 'MapError').

addStaleFallback remembers the last successful result per key and returns it when a call fails. Unlike a cache, every call still goes to the dependency; the stored value is only used on failure.

const
const pipeline: ResiliencePipeline<Profile, TimeoutRejectedError>
pipeline
= new
new ResiliencePipelineBuilder<Profile, never, never>(options?: ResiliencePipelineBuilderOptions): ResiliencePipelineBuilder<Profile, 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
<
type Profile = {
id: string;
name: string;
}
Profile
>()
.
ResiliencePipelineBuilder<Profile, never, never>.addStaleFallback(options: StaleFallbackOptions<Profile>): ResiliencePipelineBuilder<Profile, never, never>

Serve the last good value when a call fails (no caching of fresh calls): tenace's withStaleFallback.

addStaleFallback
({
StaleFallbackOptions<Profile>.maxAge: Duration

How old a last-good value may be and still be served.

maxAge
: '1h',
onStaleServed?: ((args: OnStaleServedArgs<Profile>) => MaybePromise<void>) | undefined
onStaleServed
: ({
age: number

ms since the value was stored.

age
}) =>
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.warn(message?: any, ...optionalParams: any[]): void (+1 overload)

The console.warn() function is an alias for

error

.

@since ― v0.1.100

warn
(`stale profile, ${
age: number

ms since the value was stored.

age
} ms old`) })
.
ResiliencePipelineBuilder<Profile, never, never>.addTimeout(options: Duration | TimeoutOptions): Next<Profile, never, never, TimeoutRejectedError>

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

addTimeout
('2s')
.
ResiliencePipelineBuilder<Profile, TimeoutRejectedError, never>.build(): ResiliencePipeline<Profile, TimeoutRejectedError>
build
();
// The key defaults to operationKey: without one, nothing is remembered.
const
const profile: Profile
profile
= await
const pipeline: ResiliencePipeline<Profile, TimeoutRejectedError>
pipeline
.
ResiliencePipeline<Profile, TimeoutRejectedError>.execute<Profile>(callback: (context: ResilienceContext) => MaybePromise<Profile>, options?: ExecuteOptions): Promise<Profile>

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 getProfile(id: string, signal: AbortSignal): Promise<Profile>
getProfile
('42',
signal: AbortSignal

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

signal
), {
ExecuteOptions.operationKey?: string | undefined
operationKey
: 'profile:42' });
Option Type Default Description
name string 'StaleFallback' Strategy name.
maxAge Duration required How old a remembered value may be and still be served.
key (context) => string | undefined context.operationKey Key per call. undefined bypasses the strategy.
maxEntries number 1000 Keys kept in the default in-memory store (LRU).
store CacheStore MemoryCacheStore Where values live, e.g. RedisCacheStore to share them.
shouldServeStale (args) => boolean (or a promise) or PredicateBuilder every failure except aborts and 4xx (408/429 included) Failures a stale value may hide.
onStaleServed (args) => void none Gets key, value, age (ms) and the hidden failure’s outcome.

Concurrent calls are not merged. Only successful results are stored.

Telemetry event Callback Strategy
OnFallback onFallback addFallback, addFallbackOn
OnStaleServed onStaleServed addStaleFallback
OnCacheMiss, OnCacheStoreFailed none addStaleFallback (it is built on the cache strategy: every call is a miss)

Fallback raises no error of its own. A throwing fallbackAction fails the execution with its error.

  • Expecting it to catch 4xx. The default skips client errors. Add shouldHandle if a 404 should become a default value.
  • Hiding an open circuit from your metrics. Count fallbacks with onFallback or the OnFallback event.