SkillAgentSearch skills...

Opossum

Node.js circuit breaker - fails fast ⚡️

Install / Use

/learn @nodeshift/Opossum
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

opossum

Node.js CI Coverage Status Known Vulnerabilities Dependency Status

Opossum is a Node.js circuit breaker that executes asynchronous functions and monitors their execution status. When things start failing, opossum plays dead and fails fast. If you want, you can provide a fallback function to be executed when in the failure state.

For more about the circuit breaker pattern, there are lots of resources on the web - search it! Fowler's blog post is one place to start reading.

| | Project Info | | --------------- | ------------- | | License: | Apache-2.0 | | Documentation: | https://nodeshift.dev/opossum/ | | Typings: | https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/opossum | Issue tracker: | https://github.com/nodeshift/opossum/issues | | Engines: | Node.js >= 20 |

Usage

Let's say you've got an API that depends on something that might fail - a network operation, or disk read, for example. Wrap those functions up in a CircuitBreaker and you have control over your destiny.

const CircuitBreaker = require('opossum');

function asyncFunctionThatCouldFail(x, y) {
  return new Promise((resolve, reject) => {
    // Do something, maybe on the network or a disk
  });
}

const options = {
  timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
  errorThresholdPercentage: 50, // When 50% of requests fail, trip the circuit
  resetTimeout: 30000 // After 30 seconds, try again.
};
const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);

breaker.fire(x, y)
  .then(console.log)
  .catch(console.error);

AbortController support

You can provide an AbortController (https://developer.mozilla.org/en-US/docs/Web/API/AbortController, https://nodejs.org/docs/latest/api/globals.html#globals_class_abortcontroller) for aborting on going request upon reaching Opossum timeout.

const CircuitBreaker = require('opossum');
const http = require('http');

function asyncFunctionThatCouldFail(abortSignal, x, y) {
  return new Promise((resolve, reject) => {
    http.get(
      'http://httpbin.org/delay/10',
      { signal: abortSignal },
      (res) => {
        if(res.statusCode < 300) {
          resolve(res.statusCode);
          return;
        }

        reject(res.statusCode);
      }
    );
  });
}

const abortController = new AbortController();
const options = {
  abortController,
  timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
};
const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);

breaker.fire(abortController.signal)
  .then(console.log)
  .catch(console.error);

Auto Renew AbortController

The autoRenewAbortController option allows the automatic renewal of the AbortController when the circuit breaker transitions into the halfOpen or closed states. This feature ensures that the AbortController can be reused properly for ongoing requests without manual intervention.

const CircuitBreaker = require('opossum');
const http = require('http');

function asyncFunctionThatCouldFail(abortSignal, x, y) {
  return new Promise((resolve, reject) => {
    http.get(
      'http://httpbin.org/delay/10',
      { signal: abortSignal },
      (res) => {
        if(res.statusCode < 300) {
          resolve(res.statusCode);
          return;
        }

        reject(res.statusCode);
      }
    );
  });
}

const abortController = new AbortController();
const options = {
  autoRenewAbortController: true,
  timeout: 3000, // If our function takes longer than 3 seconds, trigger a failure
};
const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);

const signal = breaker.getSignal();
breaker.fire(signal)
  .then(console.log)
  .catch(console.error);

Fallback

You can also provide a fallback function that will be executed in the event of failure. To take some action when the fallback is performed, listen for the fallback event.

const breaker = new CircuitBreaker(asyncFunctionThatCouldFail, options);
// if asyncFunctionThatCouldFail starts to fail, firing the breaker
// will trigger our fallback function
breaker.fallback(() => 'Sorry, out of service right now');
breaker.on('fallback', (result) => reportFallbackEvent(result));

Once the circuit has opened, a timeout is set based on options.resetTimeout. When the resetTimeout expires, opossum will enter the halfOpen state. Once in the halfOpen state, the next time the circuit is fired, the circuit's action will be executed again. If successful, the circuit will close and emit the close event. If the action fails or times out, it immediately re-enters the open state.

When a fallback function is triggered, it's considered a failure, and the fallback function will continue to be executed until the breaker is closed.

The fallback function accepts the same parameters as the fire function:

const delay = (delay, a, b, c) =>
  new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, delay);
  });

const breaker = new CircuitBreaker(delay);
breaker.fire(20000, 1, 2, 3);
breaker.fallback((delay, a, b, c) => `Sorry, out of service right now. But your parameters are: ${delay}, ${a}, ${b} and ${c}`);

Breaker State Initialization

There may be times where you will need to initialize the state of a Circuit Breaker. Primary use cases for this are in a serverless environment such as Knative or AWS Lambda, or any container based platform, where the container being deployed is ephemeral.

The toJSON method is a helper function to get the current state and status of a breaker:

const breakerState = breaker.toJSON();

This will return an object that might look similar to this:

{
  state: {
    enabled: true,
    name: 'functionName'
    closed: true,
    open: false,
    halfOpen: false,
    warmUp: false,
    shutdown: false
  },
  status: {
    ...
  }
};

A new circuit breaker instance can be created with this state by passing this object in:

const breaker = new CircuitBreaker({state: state});

Status Initialization

There may also be times where you will need to pre-populate the stats of the Circuit Breaker Status Object. Primary use cases for this are also in a serverless environment such as Knative or AWS Lambda, or any container based platform, where the container being deployed is ephemeral.

Getting the existing cumulative stats for a breaker can be done like this:

const stats = breaker.stats;

stats will be an object that might look similar to this:

{
  failures: 11,
  fallbacks: 0,
  successes: 5,
  rejects: 0,
  fires: 16,
  timeouts: 0,
  cacheHits: 0,
  cacheMisses: 0,
  coalesceCacheHits: 0,
  coalesceCacheMisses: 0,
  semaphoreRejections: 0,
  percentiles: {
    '0': 0,
    '1': 0,
    '0.25': 0,
    '0.5': 0,
    '0.75': 0,
    '0.9': 0,
    '0.95': 0,
    '0.99': 0,
    '0.995': 0
  },
  latencyTimes: [ 0 ],
  latencyMean: 0
}

To then re-import those stats, first create a new Status object with the previous stats and then pass that as an option to the CircuitBreaker constructor:

const statusOptions = {
  stats: {....}
};

const newStatus = CircuitBreaker.newStatus(statusOptions);

const breaker = new CircuitBreaker({status: newStatus});

Browser

Opossum really shines in a browser. You can use it to guard against network failures in your AJAX calls.

We recommend using webpack to bundle your applications, since it does not have the effect of polluting the window object with a global. However, if you need it, you can access a circuitBreaker function in the global namespace by doing something similar to what is shown in the below example.

Here is an example using hapi.js. See the opossum-examples repository for more detail.

Include opossum.js in your HTML file.

<html>
<head>
  <title>My Super App</title>
  <script type='text/javascript' src="/jquery.js"></script>
  <script type='text/javascript' src="/opossum.js"></script>
  <script type='text/javascript' src="/app.js"></script>
<body>
...
</body>
</head>
</html>

In your application, set a route to the file, pointing to node_modules/opossum/dist/opossum-min.js.

// server.js
const server = new Hapi.Server();
server.register(require('inert', (err) => possibleError(err)));
server.route({
  method: 'GET',
  path: '/opossum.js',
  handler: {
    file: {
      path: path.join(__dirname, 'node_modules', 'opossum', 'dist', 'opossum-min.js'),
    }
  }
});

In the browser's global scope will be a CircuitBreaker constructor. Use it to create circuit breakers, guarding against network failures in your REST API calls.

// app.js
const route = 'https://example-service.com/rest/route';
const circuitBreakerOptions = {
  timeout: 500,
  errorThresholdPercentage: 50,
  resetTimeout: 5000
};

const breaker = new CircuitBreaker(() => $.get(route), circuitBreakerOptions);
breaker.fallback(() => `${route} unavailable right now. Try later.`));
breaker.on('success', (result) => $(element).append(JSON.stringify(result)}));

$(() => {
  $('#serviceButton').click(() => breaker.fire().catch((e) => console.error(e)));
});

Events

A CircuitBreaker will emit events for important things that occur. Here are the events you can listen for.

  • fire - emitted when the bre
View on GitHub
GitHub Stars1.6k
CategoryDevelopment
Updated14h ago
Forks111

Languages

JavaScript

Security Score

100/100

Audited on Apr 1, 2026

No findings