Skip to main content

DI Integration

The circuitBreakerFactoryResolverDiFactory function creates an ICircuitBreakerFactoryResolver whose underlying resolver is resolved from a DI container.

The returned proxy implements both ICircuitBreakerFactoryResolver and ICircuitBreakerFactory:

  • use(adapterName?) selects an adapter on the resolved CircuitBreakerFactoryResolver.
  • create(key, settings?) creates a circuit breaker through the resolver's default adapter.

The proxy is wired to the container during container.init(). Create it before calling init(); calling use() or create() before the container is initialized throws an error.

Initial configuration​

Set up the memory and Redis circuit-breaker adapters, create the container, then register the CircuitBreakerFactoryResolver and wrap it with circuitBreakerFactoryResolverDiFactory:

1. Set up the memory circuit-breaker adapter​

./samples/memory-circuit-breaker-adapter.ts
import { DatabaseCircuitBreakerAdapter } from "eridu-tech/circuit-breaker/database-circuit-breaker-adapter";
import { MemoryCircuitBreakerStorageAdapter } from "eridu-tech/circuit-breaker/memory-circuit-breaker-storage-adapter";

export const memoryCircuitBreakerAdapter = new DatabaseCircuitBreakerAdapter({
adapter: new MemoryCircuitBreakerStorageAdapter(),
});

2. Set up the Redis circuit-breaker adapter​

./samples/redis-circuit-breaker-adapter.ts
import { RedisCircuitBreakerAdapter } from "eridu-tech/circuit-breaker/redis-circuit-breaker-adapter";
import { Redis } from "ioredis";

export const redisCircuitBreakerAdapter = new RedisCircuitBreakerAdapter({
database: new Redis("YOUR_REDIS_CONNECTION_STRING"),
});

3. Create the dependency injection container​

./samples/container.ts
import { Container } from "eridu-tech/di";
import { ExecutionContext } from "eridu-tech/execution-context";
import { AlsExecutionContextAdapter } from "eridu-tech/execution-context/als-execution-context-adapter";

export const executionContext = new ExecutionContext(
new AlsExecutionContextAdapter(),
);
export const container = new Container({ executionContext });

4. Register and initialize the circuit-breaker factory resolver​

./samples/circuit-breaker-factory-resolver-di-factory.ts
import { CircuitBreakerFactoryResolver } from "eridu-tech/circuit-breaker";
import { ProxyCircuitBreakerFactoryResolver } from "eridu-tech/circuit-breaker/di";
import { container } from "./container.js";
import { memoryCircuitBreakerAdapter } from "./memory-circuit-breaker-adapter.js";
import { redisCircuitBreakerAdapter } from "./redis-circuit-breaker-adapter.js";
import { SuperJsonSerde } from "eridu-tech/serde/super-json-serde";

type Adapters = "memory" | "redis";

const serde = new SuperJsonSerde();
const circuitBreakerFactoryResolver =
new CircuitBreakerFactoryResolver<Adapters>({
serde,
adapters: {
memory: memoryCircuitBreakerAdapter,
redis: redisCircuitBreakerAdapter,
},
defaultAdapter: "memory",
});

await circuitBreakerFactoryResolver.init();

container.registerValue({
token: CircuitBreakerFactoryResolver,
value: circuitBreakerFactoryResolver,
});

// Create the proxy before container.init()
export const circuitBreakerFactory =
new ProxyCircuitBreakerFactoryResolver<Adapters>({
container,
resolverToken: CircuitBreakerFactoryResolver,
});

await container.init();

Usage​

1. Using the default adapter​

./samples/circuit-breaker-factory-resolver-di-factory-default-adapter.ts
import { circuitBreakerFactory } from "./circuit-breaker-factory-resolver-di-factory.js";

// Uses the adapter configured as the default (memory)
await circuitBreakerFactory.create("shared-resource").runOrFail(async () => {
// code to run
});

2. Specifying an adapter explicitly​

./samples/circuit-breaker-factory-resolver-di-factory-specific-adapter.ts
import { circuitBreakerFactory } from "./circuit-breaker-factory-resolver-di-factory.js";

// Uses the Redis adapter
await circuitBreakerFactory
.use("redis")
.create("shared-resource")
.runOrFail(async () => {
// code to run
});

Using it with the middlewares​

Because the proxy implements ICircuitBreakerFactoryResolver, it can be passed straight to the circuit-breaker middleware factories:

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

const withCircuitBreaker = withCircuitBreakerFactory(circuitBreakerFactory);

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

const protectedCall = use(
callExternalApi,
withCircuitBreaker.use("redis")({
key: ([endpoint]) => `api:${endpoint}`,
}),
);

await protectedCall("users");

Further information​

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