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:
- Closed: Requests pass through normally. Failures are counted.
- Open: If failures exceed a threshold, requests fail immediately without hitting the network.
- Half-Open: After a cooldown period, a limited number of requests are allowed through to check if the service has recovered.
Step 1: Installation
Install Axios along with opossum, a battle-tested
circuit breaker library for Node.js:
npm install axios opossumStep 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
- Set Client Timeouts: Always configure
timeoutin Axios instances alongside the breaker timeout. - Filter 4xx Errors: Use
errorFilterso client errors do not inadvertently open the circuit. - Define Graceful Fallbacks: Return cached data or meaningful error structures when downstream services are offline.
- Log State Transitions: Listen to
openandhalfOpenevents to monitor dependency health in real time.