Circuit Breaker Plugin
A distributed circuit breaker (closed / open / half-open) that stops calls to a failing dependency, gives it time to recover, and probes before restoring traffic — consistently across all application instances via Redis.
Overview
When a downstream dependency (payment gateway, third-party API, another service) starts failing, hammering it makes things worse and ties up your own resources. The circuit breaker counts failures and, once a threshold is crossed, opens — rejecting calls immediately instead of waiting on timeouts. After a cooldown it moves to half-open, letting a limited number of probe calls through; if they succeed it closes again.
| Challenge | Without a breaker | With the Circuit Breaker Plugin |
|---|---|---|
| Failing dependency | Every call waits for a timeout | Calls fail fast while OPEN |
| Recovery | Thundering herd on recovery | Controlled probes (HALF_OPEN) |
| Multi-instance | Each instance decides alone | Shared state across instances via Redis |
Key Features
- Distributed state — the breaker state lives in Redis, so all instances agree.
- Pure, time-injected core — the state machine (
CircuitBreakerState) takes an explicitnow; no hiddenDate.now(), fully deterministic and unit-testable. - Atomic transitions — state changes run in Lua for correctness under concurrency.
- Works anywhere — the
@WithCircuitBreakerdecorator wraps any Injectable method (proxy-based, not a controller guard). - Fallbacks — return a cached/default value instead of throwing while OPEN.
- fail-open / fail-closed — choose availability or strictness when the state store itself is unavailable.
Installation
bash
npm install @nestjs-redisx/core @nestjs-redisx/circuit-breaker ioredisbash
npm install @nestjs-redisx/core @nestjs-redisx/circuit-breaker redisBasic Configuration
typescript
import { Module } from '@nestjs/common';
import { RedisModule } from '@nestjs-redisx/core';
import { CircuitBreakerPlugin } from '@nestjs-redisx/circuit-breaker';
@Module({
imports: [
RedisModule.forRoot({
clients: {
host: 'localhost',
port: 6379,
},
plugins: [
new CircuitBreakerPlugin({
failureThreshold: 5, // trip after 5 failures...
windowMs: 10000, // ...within a 10s rolling window
openDurationMs: 30000, // stay OPEN for 30s before probing
halfOpenMaxCalls: 1, // allow 1 probe while HALF_OPEN
successThreshold: 1, // 1 successful probe closes the breaker
probeTimeoutMs: 30000, // reclaim a probe slot if its outcome is never recorded
}),
],
}),
],
})
export class AppModule {}Usage with the Decorator
typescript
import { Injectable } from '@nestjs/common';
import { WithCircuitBreaker } from '@nestjs-redisx/circuit-breaker';
import { PaymentGateway, UsersApi, UserCache, Charge, User } from './types';
@Injectable()
export class PaymentsService {
constructor(
private readonly gateway: PaymentGateway,
private readonly usersApi: UsersApi,
private readonly userCache: UserCache,
) {}
// Trips after repeated failures; throws CircuitBreakerOpenError while OPEN.
@WithCircuitBreaker({ key: 'stripe', failureThreshold: 5, openDurationMs: 30000 })
async charge(charge: Charge): Promise<{ ok: boolean }> {
return this.gateway.charge(charge);
}
// Key interpolated from the first argument; falls back to cache when OPEN.
@WithCircuitBreaker({
key: 'users-api:{0}',
fallback: (id: string) => ({ id, name: 'cached' }),
})
async getUser(id: string): Promise<User> {
return this.usersApi.getUser(id);
}
// Skip execution (resolve to undefined) instead of throwing while OPEN.
@WithCircuitBreaker({ key: 'users-api', onOpen: 'skip' })
async warmCache(id: string): Promise<User | null> {
return this.userCache.get(id);
}
// Bypass the breaker entirely for trusted/internal calls via skip().
@WithCircuitBreaker({
key: 'users-api',
skip: (id: string, internal?: boolean) => internal === true,
})
async getUserMaybeInternal(id: string, internal?: boolean): Promise<User> {
return this.usersApi.getUser(id);
}
}States
Documentation
| Topic | Description |
|---|---|
| Core Concepts | States and when to use a breaker |
| Configuration | Options and defaults |
| @WithCircuitBreaker Decorator | Method-level breaking |
| Service API | Programmatic execute / manual recording |
| Algorithm | States, window, cooldown, probes |
| Monitoring | Observing circuit state |
| Recipes | Fallbacks and patterns |
| Testing | Testing breaker-guarded code |
| Troubleshooting | Debugging common issues |