# Circuit breaker pattern for external API dependencies

- **Pattern:** `ab-001283` (`error-resilience.network-api-resilience.circuit-breaker-pattern`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001283
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001283)

## Why it matters

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.

## Severity rationale

Medium because without circuit-breaking, a single downstream API failure causes cascading thread exhaustion or queue saturation across the entire application.

## Remediation

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.

```ts
// 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.

## Detection

- **ID:** `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:

  ```ts
  // lib/circuit-breaker.ts — circuit breaker pattern
  class CircuitBreaker { constructor(private fn: Function, private threshold = 5, private timeout = 30000) {} }
  ```

  ```ts
  // 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'
      }
    }
  }
  ```

## External references

- cwe CWE-703
- iso-25010 reliability.fault-tolerance

Taxons: error-resilience

HTML version: https://auditbuffet.com/patterns/ab-001283
