Aviary

Getting Started

Install nestjs-resilience, wrap a flaky call with a composed policy, then register the module and use it through dependency injection.

Getting nestjs-resilience running is three steps: install the package, compose a policy, and (optionally) register ResilienceModule.forRoot() so you can reach the policies through DI. You can use the policies entirely standalone — the NestJS surfaces are a convenience, not a requirement.


Prerequisites

  • Node.js 20+
  • NestJS 10+ (both v10 and v11 are supported)
  • TypeScript 5+ with experimentalDecorators and emitDecoratorMetadata enabled

Install

pnpm add @dudousxd/nestjs-resilience

@nestjs/common and @nestjs/core are peer dependencies — you already have them in a NestJS app. @dudousxd/nestjs-diagnostics is an optional peer: install it only if you want state transitions on the diagnostics channel (see Integrations).

Wrap a call with a policy

Every policy exposes execute(fn). Compose several with wrap() — outermost first — and run your operation inside:

import { wrap, timeout, retry, exponential, circuitBreaker, InMemoryResilienceStore } from '@dudousxd/nestjs-resilience';

const store = new InMemoryResilienceStore();

const charge = wrap(
  timeout(2_000),                                              // give up after 2s
  retry({ attempts: 3, backoff: exponential(100) }),          // up to 3 tries, 100ms→200ms→400ms
  circuitBreaker({ key: 'payments', store, threshold: 5, cooldownMs: 30_000 }),
);

const result = await charge.execute(() => chargeCard(order));

Your function receives a PolicyContext ({ signal, attempt }) if you want it — wire the AbortSignal into fetch/your client to make timeouts actually cancel work:

await charge.execute(({ signal }) => fetch(url, { signal }));

Register the module (optional)

To reach policies through DI — and to enable the diagnostics emission — register the module once. It's global by default:

app.module.ts
import { ResilienceModule, retry, exponential, timeout, wrap } from '@dudousxd/nestjs-resilience';

@Module({
  imports: [
    ResilienceModule.forRoot({
      // named policies you can run by key
      policies: {
        payments: () => wrap(timeout(2_000), retry({ attempts: 3, backoff: exponential(100) })),
      },
    }),
  ],
})
export class AppModule {}

Then inject ResilienceService and run a named policy:

import { ResilienceService } from '@dudousxd/nestjs-resilience';

@Injectable()
export class BillingService {
  constructor(private readonly resilience: ResilienceService) {}

  charge(order: Order) {
    return this.resilience.execute('payments', () => chargeCard(order));
  }
}

By default the module uses an in-memory store and emits diagnostics events. To share circuit state across instances, pass a distributed store — see Stores. To turn emission off, pass emit: false.

Where to go next

On this page