Skip to main content

CircuitBreaker middlewares

Initial configuration​

To begin using the circuit-breaker middlewares, you'll need to create and configure a CircuitBreakerFactoryResolver instance:

./samples/circuit-breaker.ts
import { CircuitBreakerFactoryResolver } from "eridu-tech/circuit-breaker";
import { DatabaseCircuitBreakerAdapter } from "eridu-tech/circuit-breaker/database-circuit-breaker-adapter";
import { MemoryCircuitBreakerStorageAdapter } from "eridu-tech/circuit-breaker/memory-circuit-breaker-storage-adapter";
import { RedisCircuitBreakerAdapter } from "eridu-tech/circuit-breaker/redis-circuit-breaker-adapter";
import { SuperJsonSerde } from "eridu-tech/serde/super-json-serde";
import { Redis } from "ioredis";

const serde = new SuperJsonSerde();
export const circuitBreakerFactoryResolver = new CircuitBreakerFactoryResolver({
serde,
adapters: {
memory: new DatabaseCircuitBreakerAdapter({
adapter: new MemoryCircuitBreakerStorageAdapter(),
}),
redis: new RedisCircuitBreakerAdapter({
database: new Redis("YOUR_REDIS_CONNECTION_STRING"),
}),
},
defaultAdapter: "memory",
});

await circuitBreakerFactoryResolver.init();

withCircuitBreakerFactory middleware​

The CircuitBreaker middleware wraps function calls with a circuit-breaker, providing fault tolerance for distributed systems. Each unique key (derived from the function's arguments) gets its own circuit instance. When the circuit is open, the wrapped function is not called and an error is thrown instead, preventing cascading failures.

Usage​

./samples/with-circuit-breaker.ts
import { withCircuitBreakerFactory } from "eridu-tech/circuit-breaker/middlewares";
import { use } from "eridu-tech/middleware";
import { circuitBreakerFactoryResolver } from "./circuit-breaker.js";

const withCircuitBreaker = withCircuitBreakerFactory(
circuitBreakerFactoryResolver,
);

const callExternalApi = async (endpoint: string): Promise<unknown> => {
const response = await fetch(`https://api.example.com/${endpoint}`);
return response.json();
};

// Wrap with circuit-breaker using the default adapter (`memory`)
const protectedCall = use(
callExternalApi,
withCircuitBreaker({
key: ([endpoint]) => `api:${endpoint}`,
}),
);

// Wrap with circuit-breaker using a specific adapter (`redis`)
const protectedCallOnRedis = use(
callExternalApi,
withCircuitBreaker.use("redis")({
key: ([endpoint]) => `api:${endpoint}`,
}),
);

await protectedCall("users"); // Succeeds or opens the circuit on repeated failures
await protectedCallOnRedis("users"); // Uses the Redis adapter

Calling withCircuitBreaker() directly uses the resolver's default adapter (memory). Use withCircuitBreaker.use("redis") to run the wrapped function through a specific registered adapter.

info

Here is a complete list of settings for the withCircuitBreaker function.

Settings​

OptionTypeDescription
keyInvocable<TParameters, string>A function that produces a unique identifier for the circuit from the wrapped function's arguments. Each unique key gets its own circuit state
errorPolicyErrorPolicyDetermines which errors count toward opening the circuit. Defaults to treating all errors as failures
triggerCircuitBreakerTriggerOptional custom trigger that determines when the circuit should open. Defaults to the built-in trigger
slowCallTimeITimeSpanOptional duration above which a call is considered slow. Exceeding it counts toward opening the circuit (if configured in the trigger)

withHttpCircuitBreakerError middleware​

The withHttpCircuitBreakerError middleware converts every circuit-breaker error thrown by the wrapped endpoint or downstream middleware into an HttpError, so the request fails with a meaningful status code instead of a generic 500. Errors that are not circuit-breaker errors are rethrown untouched, and the original error is preserved as the cause.

Usage​

./samples/with-http-circuit-breaker-error.ts
import { OpenCircuitBreakerError } from "eridu-tech/circuit-breaker/contracts";
import { withHttpCircuitBreakerError } from "eridu-tech/circuit-breaker/middlewares";
import { HttpRouter, defaultHttpRouterAdapter } from "eridu-tech/http-router";

const router = new HttpRouter({
router: defaultHttpRouterAdapter(),
});

router.use(withHttpCircuitBreakerError());

router.endpoint({
url: "/payments/1",
method: ["POST"],
handler: async () => {
// A 503 response when the circuit is open
throw OpenCircuitBreakerError.create("payments");
},
});

Status codes​

ErrorsStatus
OpenCircuitBreakerError, IsolatedCircuitBreakerError503
UnableToGetStateCircuitBreakerError, UnableToUpdateStateCircuitBreakerError, UnableToIsolateCircuitBreakerError, UnableToTrackFailureCircuitBreakerError, UnableToTrackSuccessCircuitBreakerError, UnableToResetCircuitBreakerError503

Every circuit-breaker error means the circuit is not accepting calls or the underlying adapter failed, so they all map to 503 and the same request can be retried later. The original error is preserved as the cause, and no identifiers are exposed in the response body.

Further information​

For further information refer to eridu-tech/circuit-breaker API docs.