SkillAgentSearch skills...

Cockatiel

🐦 A resilience and fault-handling library. Supports Backoffs, Retries, Circuit Breakers, Timeouts, Bulkhead Isolation, and Fallbacks.

Install / Use

/learn @connor4312/Cockatiel

README

Cockatiel

Actions Status npm bundle size No dependencies

Cockatiel is resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback. .NET has Polly, a wonderful one-stop shop for all your fault handling needs--I missed having such a library for my JavaScript projects, and grew tired of copy-pasting retry logic between my projects. Hence, this module!

npm install --save cockatiel

Then go forth with confidence:

import {
  ConsecutiveBreaker,
  ExponentialBackoff,
  retry,
  handleAll,
  circuitBreaker,
  wrap,
} from 'cockatiel';
import { database } from './my-db';

// Create a retry policy that'll try whatever function we execute 3
// times with a randomized exponential backoff.
const retryPolicy = retry(handleAll, { maxAttempts: 3, backoff: new ExponentialBackoff() });

// Create a circuit breaker that'll stop calling the executed function for 10
// seconds if it fails 5 times in a row. This can give time for e.g. a database
// to recover without getting tons of traffic.
const circuitBreakerPolicy = circuitBreaker(handleAll, {
  halfOpenAfter: 10 * 1000,
  breaker: new ConsecutiveBreaker(5),
});

// Combine these! Create a policy that retries 3 times, calling through the circuit breaker
const retryWithBreaker = wrap(retryPolicy, circuitBreakerPolicy);

exports.handleRequest = async (req, res) => {
  // Call your database safely!
  const data = await retryWithBreaker.execute(() => database.getInfo(req.params.id));
  return res.json(data);
};

I recommend reading the Polly wiki for more information for details and mechanics around the patterns we provide.

Table of Contents

IPolicy (the shape of a policy)

All Cockatiel fault handling policies (fallbacks, circuit breakers, bulkheads, timeouts, retries) adhere to the same interface. In TypeScript, this is given as:

export interface IPolicy<ContextType extends { signal: AbortSignal }> {
  /**
   * Fires on the policy when a request successfully completes and some
   * successful value will be returned. In a retry policy, this is fired once
   * even if the request took multiple retries to succeed.
   */
  readonly onSuccess: Event<ISuccessEvent>;

  /**
   * Fires on the policy when a request fails *due to a handled reason* fails
   * and will give rejection to the called.
   */
  readonly onFailure: Event<IFailureEvent>;

  /**
   * Runs the function through behavior specified by the policy.
   */
  execute<T>(fn: (context: ContextType) => PromiseLike<T> | T, signal?: AbortSignal): Promise<T>;
}

If you don't read TypeScript often, here's what it means:

  • There are two events, onSuccess/onFailure, that are called when a call succeeds or fails. Note that onFailure only is called if a handled error is thrown.

    As a design decision, Cockatiel won't assume all thrown errors are actually failures unless you tell us. For example, in your application you might have errors thrown if the user submits invalid input, and triggering fault handling behavior for this reason would not be desirable!

  • There's an execute function that you can use to "wrap" your own function. Anything you return from that function is returned, in a promise, from execute. You can optionally pass an abort signal to the execute() function, and the function will always be called with an object at least containing an abort signal (some policies might add extra metadata for you).

Policy

The Policy defines how errors and results are handled. Everything in Cockatiel ultimately deals with handling errors or bad results. The Policy sets up how

handleAll

A generic policy to handle all errors.

import { handleAll } from 'cockatiel';

retry(handleAll /* ... */);

handleType(ctor[, filter]) / policy.orType(ctor[, filter])

Tells the policy to handle errors of the given type, passing in the contructor. If a filter function is also passed, we'll only handle errors if that also returns true.

import { handleType } from 'cockatiel';

handleType(NetworkError).orType(HttpError, err => err.statusCode === 503);
// ...

handleWhen(filter) / policy.orWhen(filter)

Tells the policy to handle any error for which the filter returns truthy

import { handleWhen } from 'cockatiel';

handleWhen(err => err instanceof NetworkError).orWhen(err => err.shouldRetry === true);
// ...

handleResultType(ctor[, filter]) / policy.orResultType(ctor[, filter])

Tells the policy to treat certain return values of the function as errors--retrying if they appear, for instance. Results will be retried if they're an instance of the given class. If a filter function is also passed, we'll only treat return values as errors if that also returns true.

import { handleResultType } from 'cockatiel';

handleResultType(ReturnedNetworkError).orResultType(HttpResult, res => res.statusCode === 503);
// ...

handleWhenResult(filter) / policy.orWhenResult(filter)

Tells the policy to treat certain return values of the function as errors--retrying if they appear, for instance. Results will be retried the filter function returns true.

import { handleWhenResult } from 'cockatiel';

handleWhenResult(res => res.statusCode === 503).orWhenResult(res => res.statusCode === 429);
// ...

wrap(...policies)

Wraps the given set of policies into a single policy. For instance, this:

const result = await retry.execute(() =>
  breaker.execute(() => timeout.execute(({ signal }) => getData(signal))),
);

Is the equivalent to:

import { wrap } from 'cockatiel';

const result = await wrap(retry, breaker, timeout).execute(({ signal }) => getData(signal));

The context argument passed to the executed function is the merged object of all previous policies. So for instance, in the above example you'll get the abort signal from the TimeoutPolicy as well as the attempt number from the RetryPolicy:

import { wrap } from 'cockatiel';

wrap(retry, breaker, timeout).execute(context => {
  console.log(context);
  // => { attem

Related Skills

View on GitHub
GitHub Stars1.8k
CategoryCustomer
Updated6d ago
Forks61

Languages

TypeScript

Security Score

100/100

Audited on Mar 17, 2026

No findings