Your app calls an external API. The API is down.
Your code: "I'll keep retrying forever."
User: "Your app is frozen."
Circuit breaker prevents this.
The Pattern
Imagine a circuit breaker in your house. When too much current flows, the breaker trips. Cuts the circuit. Prevents fire.
Same idea for APIs.
States:
- CLOSED: API works, requests go through
- OPEN: API is down, requests fail fast (no retry)
- HALF_OPEN: API might be back, try a test request
Flow:
Request → API works → CLOSED (pass through)
Request → API fails → count failures
Failures > threshold → OPEN (fail fast, no retry)
Wait 30 seconds → HALF_OPEN (test request)
Test succeeds → CLOSED (back to normal)
Test fails → OPEN (not ready yet)
Why This Matters
Without circuit breaker:
API is down.
Code: "Try again"
Code: "Try again"
Code: "Try again"
(30 seconds of retries)
User: "Your app is broken"
With circuit breaker:
API is down.
Code: "Return error immediately"
User: "That service is unavailable, try later"
(Fails fast, doesn't waste time)
Plus: circuit breaker detects when API is back online and routes traffic to it again.
Implementation (30 Minutes)
class CircuitBreaker {
constructor(fn, threshold = 5, timeout = 30000) {
this.fn = fn;
this.failures = 0;
this.threshold = threshold;
this.timeout = timeout;
this.state = 'CLOSED';
this.nextAttempt = Date.now();
}
async call() {
if (this.state === 'OPEN') {
if (Date.now() < this.nextAttempt) {
throw new Error('Circuit breaker OPEN');
}
this.state = 'HALF_OPEN';
}
try {
const result = await this.fn();
this.onSuccess();
return result;
} catch (e) {
this.onFailure();
throw e;
}
}
onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}
onFailure() {
this.failures++;
if (this.failures >= this.threshold) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + this.timeout;
}
}
}
// Usage
const breaker = new CircuitBreaker(
() => externalAPI.request(),
5, // Open after 5 failures
30000 // Try again after 30 seconds
);
try {
const result = await breaker.call();
} catch (e) {
if (e.message === 'Circuit breaker OPEN') {
return { error: 'Service unavailable, try again later' };
}
return { error: 'Request failed' };
}
Real Example
Payment processing called an external payment gateway.
Gateway had occasional outages (1-2 per week, usually brief).
Without circuit breaker:
- Outage starts
- App tries to process payment
- Retries 10x (30 seconds)
- Payment fails
- Customer angry
With circuit breaker:
- Outage starts
- App tries payment
- Fails twice
- Circuit opens
- Next payment: instant failure with "try later"
- 30 seconds later, circuit tries again
- Gateway is back, circuit closes
- Payments flow again
Same outage, but customer experience improved drastically.
Libraries
Most languages have circuit breaker libraries:
- Node.js:
opossum - Python:
pybreaker - Go:
gobreaker - Java:
hystrix
Use them. Don't build from scratch.
Checklist
For every external API call:
- [ ] Is this call potentially slow or flaky?
- [ ] Do I have a circuit breaker?
- [ ] What's the failure threshold? (5-10 failures)
- [ ] What's the timeout before retry? (30 seconds)
- [ ] Does user see helpful message on failure?
If your app hangs when external services are down, add a circuit breaker.
Fail fast. Let users know. Try again later.
Your users will thank you.
Top comments (0)