Circuit Breaker Pattern with Axios

The circuit breaker pattern prevents an application from repeatedly executing operations that are likely to fail, shielding downstream services and preventing cascading failures. This article demonstrates how to implement the circuit breaker pattern around HTTP requests made with Axios using the popular opossum library in Node.js, covering setup, configuration, fallback handling, and state monitoring.


Why Use a Circuit Breaker with Axios?

When a remote service is degraded or unavailable, standard retry logic can overwhelm the target server or exhaust client-side resources. A circuit breaker monitors requests and operates in three states:


Step 1: Installation

Install Axios along with opossum, a battle-tested circuit breaker library for Node.js:

npm install axios opossum

Step 2: Basic Implementation

Wrap the Axios call inside an asynchronous function and pass it to a new CircuitBreaker instance:

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

// 1. Define the network call
async function fetchUserData(userId) {
  const response = await axios.get(`https://api.example.com/users/${userId}`, {
    timeout: 3000 // Ensure Axios has its own request timeout
  });
  return response.data;
}

// 2. Configure circuit breaker options
const breakerOptions = {
  timeout: 4000,                // If the action takes longer than 4s, trigger a failure
  errorThresholdPercentage: 50, // Open circuit when 50% of requests fail
  resetTimeout: 10000            // Wait 10s before switching to Half-Open
};

// 3. Instantiate the breaker
const userBreaker = new CircuitBreaker(fetchUserData, breakerOptions);

// 4. Execute requests through the breaker
async function getUser(id) {
  try {
    const data = await userBreaker.fire(id);
    return data;
  } catch (error) {
    if (userBreaker.opened) {
      console.warn('Circuit is OPEN: Request blocked immediately.');
    } else {
      console.error('Request failed:', error.message);
    }
    throw error;
  }
}

Step 3: Adding Fallback Responses

You can define a fallback function that executes whenever the circuit is open or the request fails:

userBreaker.fallback((userId, error) => {
  return {
    id: userId,
    name: 'Cached/Default User',
    isFallback: true
  };
});

// Calling fire() will now return the fallback payload instead of rejecting
const user = await userBreaker.fire('123');
console.log(user);

Step 4: Filtering Non-Transient Errors

By default, any thrown error counts toward opening the circuit. In HTTP clients like Axios, client errors (such as 400 Bad Request or 404 Not Found) should generally not trip the circuit, as they indicate client issues rather than service downtime.

Use the errorFilter option to ignore specific Axios errors:

const breakerOptions = {
  timeout: 4000,
  errorThresholdPercentage: 50,
  resetTimeout: 10000,
  errorFilter: (error) => {
    // Return true to ignore the error (do not count as failure)
    if (error.response && error.response.status >= 400 && error.response.status < 500) {
      return true;
    }
    return false; // 5xx errors or network timeouts will count as failures
  }
};

const resilientBreaker = new CircuitBreaker(fetchUserData, breakerOptions);

Step 5: Event Monitoring and Metrics

opossum emits events that can be used for logging, metrics, and alerting:

userBreaker.on('open', () => {
  console.warn(`[CIRCUIT BREAKER] State changed to OPEN for ${userBreaker.name}`);
});

userBreaker.on('close', () => {
  console.info(`[CIRCUIT BREAKER] State changed to CLOSED for ${userBreaker.name}`);
});

userBreaker.on('halfOpen', () => {
  console.info(`[CIRCUIT BREAKER] State changed to HALF-OPEN for ${userBreaker.name}`);
});

userBreaker.on('fallback', (result) => {
  console.warn(`[CIRCUIT BREAKER] Fallback executed:`, result);
});

Summary Checklist for Axios & Circuit Breakers

  1. Set Client Timeouts: Always configure timeout in Axios instances alongside the breaker timeout.
  2. Filter 4xx Errors: Use errorFilter so client errors do not inadvertently open the circuit.
  3. Define Graceful Fallbacks: Return cached data or meaningful error structures when downstream services are offline.
  4. Log State Transitions: Listen to open and halfOpen events to monitor dependency health in real time.