Skip to main content
v2.10.0·MIT

nestjs-jetstream

The NATS JetStream transport NestJS microservices need — durable, retried, traced — under the same @EventPattern decorators you already use.

npm i @horizon-republic/nestjs-jetstreamMITNode 20NestJS 10+
Why this library

Five primitives, one transport.

Each one drops in behind the decorators you already use. Same behavior in dev, staging, and prod.

At-least-once delivery, bounded retries

Every event is acknowledged after the handler returns. Failures retry with exponential backoff and land in a typed dead-letter queue if the budget is exhausted.

Broadcast fan-out across replicas

One @Broadcast() event reaches every running pod — with no double-processing on the workqueue side.

RPC over Core & JetStream

Request/reply with timeouts, typed responses, and the RecordBuilder for headers.

Distributed tracing, on by default

W3C traceparent propagated through every hop. Wire to OpenTelemetry in three lines.

Ordered delivery per partition

Stable subject keys give you single-consumer ordering without giving up horizontal scale on the rest of the workload.

orders.42orders.42 →orders.42
Live code

Configure once. Decorate handlers. Ship.

Same NestJS surface area you use today. The library does the JetStream work underneath.

app.module.tsTypeScript
import { Module } from '@nestjs/common';
import { JetstreamModule } from '@horizon-republic/nestjs-jetstream';

@Module({
  imports: [
    JetstreamModule.forRoot({
      servers: ['nats://localhost:4222'],
    }),
  ],
})
export class AppModule {}
orders.controller.tsTypeScript
import { Controller, Logger } from '@nestjs/common';
import { EventPattern, Payload } from '@nestjs/microservices';

// At-least-once. Retries on throw. Traced end-to-end.
@Controller()
export class OrdersController {
  private readonly log = new Logger(OrdersController.name);

  @EventPattern('orders.created')
  async onCreated(@Payload() order: Order) {
    await this.billing.charge(order);
    this.log.log(`charged ${order.id}`);
  }

  @MessagePattern('orders.lookup')
  lookup(@Payload() id: string) {
    return this.orders.find(id);
  }
}
Architecture

Sits between your application and the stream.

Provisions streams & consumers on boot, routes messages to decorated handlers, drains cleanly on shutdown.

PUBLISHER · NESTJSapi-gatewayclient.emit( 'orders.created', order);ClientProxy + tracingLIBRARY · @horizon-republic/nestjs-jetstreamforRoot()forFeature()StrategyCodecAck · retry · DLQ · ordered · broadcast · RPC · trace contextCONSUMER · NESTJSorders-svc@EventPattern( 'orders.created')onCreated(o) { … }ack · retry · DLQNATS SERVER · JETSTREAMstream: orders · subjects: orders.> · replicas: 3 · workqueueemitdeliver
Publish → stream → consumer. The library owns provisioning, routing, retries, and tracing on both sides.

Read the docs. Ship the code.

Five-minute quick start. Then drop into your existing NestJS module graph.