When an external API — payment gateway, SMS provider, identity service — starts returning errors, repeated synchronous retries amplify load on an already-struggling service and cascade failures throughout your application. CWE-703 (improper check for exceptional conditions) applies to this failure mode. ISO 25010 reliability.fault-tolerance requires the system to degrade gracefully under component failures; a missing circuit breaker means one downstream outage can freeze all request-handling threads or queue workers indefinitely, taking down your entire service.
Medium because without circuit-breaking, a single downstream API failure causes cascading thread exhaustion or queue saturation across the entire application.
Use the opossum library for Node.js or implement a minimal hand-rolled breaker for fetch-based calls. The breaker must track failure rate and reject calls when the circuit is open.
// lib/circuit-breaker.ts
import CircuitBreaker from 'opossum'
export function createBreaker<T>(fn: (...args: unknown[]) => Promise<T>) {
return new CircuitBreaker(fn, {
timeout: 5000, // fail after 5s
errorThresholdPercentage: 50, // open at 50% failure rate
resetTimeout: 30000, // retry after 30s
})
}
// Usage:
const breaker = createBreaker(callPaymentAPI)
const result = await breaker.fire(payload)
Wrap the three highest-traffic external integrations first: payment, auth, and any real-time data feed.
ID: error-resilience.network-api-resilience.circuit-breaker-pattern
Severity: medium
What to look for: Count all external service integrations (APIs, databases, third-party services). Enumerate which implement circuit breaker or fallback patterns vs. which fail directly on service unavailability. Search for circuit breaker implementation or library (opossum, circuitbreaker, or hand-rolled logic). Look for a pattern that tracks error rates and fails fast when a threshold is exceeded, preventing cascading failures when an external API is down.
Pass criteria: A circuit breaker is implemented for external API dependencies that opens when error rate exceeds a threshold and prevents cascading failures. At least 50% of external service integrations should implement circuit breaker or fallback patterns.
Fail criteria: No circuit breaker pattern found; all API calls are retried indefinitely without failure detection.
Skip (N/A) when: The application has no external API dependencies.
Cross-reference: For retry logic, see retry-logic-backoff. For graceful API failure, see graceful-api-failure.
Detail on fail: "No circuit breaker implemented. Failed API calls will retry indefinitely, causing slow cascading failures" or "Circuit breaker exists for payment API but not for user service API"
Remediation: Implement a circuit breaker using a library or manually:
// lib/circuit-breaker.ts — circuit breaker pattern
class CircuitBreaker { constructor(private fn: Function, private threshold = 5, private timeout = 30000) {} }
// lib/circuitBreaker.ts
interface CircuitBreakerState {
state: 'closed' | 'open' | 'half-open'
failureCount: number
lastFailureTime: number
}
class CircuitBreaker {
private state: CircuitBreakerState = {
state: 'closed',
failureCount: 0,
lastFailureTime: 0,
}
async call<T>(fn: () => Promise<T>, timeout = 5000): Promise<T> {
if (this.state.state === 'open') {
if (Date.now() - this.state.lastFailureTime > 30000) {
this.state.state = 'half-open'
} else {
throw new Error('Circuit breaker is open')
}
}
try {
const result = await Promise.race([
fn(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
),
])
this.reset()
return result as T
} catch (error) {
this.recordFailure()
throw error
}
}
private reset() {
this.state.failureCount = 0
this.state.state = 'closed'
}
private recordFailure() {
this.state.failureCount++
this.state.lastFailureTime = Date.now()
if (this.state.failureCount >= 5) {
this.state.state = 'open'
}
}
}