JAVASCRIPT

Implementing a Circuit Breaker for Resilient API Calls

Enhance API call resilience using the Circuit Breaker pattern to prevent cascading failures by temporarily blocking requests to services experiencing issues.

class CircuitBreaker {
  constructor(serviceName, failureThreshold = 3, resetTimeout = 5000) {
    this.serviceName = serviceName;
    this.failureThreshold = failureThreshold;
    this.resetTimeout = resetTimeout;
    this.failures = 0;
    this.state = 'CLOSED'; // CLOSED, OPEN, HALF-OPEN
    this.nextAttempt = Date.now();
  }

  async fire(apiCall) {
    if (this.state === 'OPEN' && this.nextAttempt > Date.now()) {
      throw new Error(`Circuit for ${this.serviceName} is OPEN. Not attempting call.`);
    }

    try {
      const result = await apiCall();
      this.success();
      return result;
    } catch (error) {
      this.fail();
      throw error;
    }
  }

  success() {
    this.failures = 0;
    this.state = 'CLOSED';
    console.log(`Circuit for ${this.serviceName} is CLOSED.`);
  }

  fail() {
    this.failures++;
    if (this.failures >= this.failureThreshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.resetTimeout;
      console.error(`Circuit for ${this.serviceName} is OPEN. Next attempt at ${new Date(this.nextAttempt).toLocaleTimeString()}`);
    } else {
      console.warn(`Circuit for ${this.serviceName}: ${this.failures}/${this.failureThreshold} failures.`);
      if (this.state === 'HALF-OPEN') {
        this.state = 'CLOSED'; // If it fails in HALF-OPEN, close it again
      }
    }
    if (this.state === 'OPEN' && Date.now() >= this.nextAttempt) {
        this.state = 'HALF-OPEN';
        console.log(`Circuit for ${this.serviceName} is now HALF-OPEN. Trying a single request.`);
    }
  }
}

// Example Usage:
const externalAPICall = async () => {
  const simulateSuccess = Math.random() > 0.3; // 70% success rate
  if (simulateSuccess) {
    return Promise.resolve({ data: 'External API data' });
  } else {
    return Promise.reject(new Error('External API failed!'));
  }
};

const myServiceCircuit = new CircuitBreaker('ExternalService', 2, 3000);

async function callExternalService() {
  try {
    const response = await myServiceCircuit.fire(externalAPICall);
    console.log('API call successful:', response);
  } catch (error) {
    console.error('API call failed or circuit open:', error.message);
  }
}

setInterval(callExternalService, 1000); // Attempt every second
How it works: The Circuit Breaker pattern enhances resilience by preventing an application from repeatedly trying to access a failing service. It has three states: `CLOSED` (normal operation), `OPEN` (requests are immediately rejected after failures exceed a threshold), and `HALF-OPEN` (after a timeout, a single test request is allowed; if it succeeds, the circuit closes, otherwise it re-opens). This prevents cascading failures, allows the failing service to recover, and provides immediate feedback to the calling application.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs