Skip to main content

How to migrate from @nestjs/microservices NATS to JetStream

This guide walks through replacing the built-in NestJS NATS transport (@nestjs/microservices package, Transport.NATS) with @horizon-republic/nestjs-jetstream. Your ClientProxy already talks to NATS — switching to JetStream is mostly configuration.

You will end up with durable delivery, automatic retries, dead letter handling, and W3C trace context for the same @EventPattern / @MessagePattern handlers you already have.

Already using this library and upgrading to a newer version?

See the Release Notes instead. This guide covers the one-time switch from @nestjs/microservices.

What changes

The semantic shift is from at-most-once to at-least-once delivery:

  • Delivery. Built-in NATS is fire-and-forget; messages are lost if no subscriber is listening. JetStream persists every event in a stream so messages survive restarts.
  • Retention. Built-in has no retention. JetStream keeps messages in a stream until acked (workqueue) or until the retention window expires.
  • Replay. Built-in does not support replay. JetStream consumers can be created to catch up on history.
  • Retries. Built-in does not retry. JetStream redelivers a message until the handler acks or max_deliver is exhausted.
  • Dead letters. Built-in silently drops failed messages. JetStream provides a configurable dead-letter flow (Dead Letter Queue).
  • Decorators. Unchanged. @EventPattern and @MessagePattern work identically.

Step 1 — Install the library

npm install @horizon-republic/nestjs-jetstream @nats-io/transport-node @nats-io/jetstream

You can remove @nestjs/microservices if no other transport (Redis, RabbitMQ, Kafka) is in use.

Step 2 — Replace module registration

Before:

import { Transport, ClientsModule } from '@nestjs/microservices';

@Module({
imports: [
ClientsModule.register([
{
name: 'NATS_SERVICE',
transport: Transport.NATS,
options: { servers: ['nats://localhost:4222'] },
},
]),
],
})
export class AppModule {}

After:

import { JetstreamModule } from '@horizon-republic/nestjs-jetstream';

@Module({
imports: [
// Once in your root module — creates the connection + consumer infrastructure
JetstreamModule.forRoot({
name: 'orders',
servers: ['nats://localhost:4222'],
}),

// Per target service, where you publish to
JetstreamModule.forFeature({ name: 'payments' }),
],
})
export class AppModule {}

The name field is the service identity used to derive stream, consumer, and subject names. See Naming Conventions for the rules.

Step 3 — Keep your handlers

No changes to handler signatures. @EventPattern and @MessagePattern work identically.

import { Controller } from '@nestjs/common';
import { EventPattern, MessagePattern, Payload } from '@nestjs/microservices';

@Controller()
export class OrdersController {
@EventPattern('order.created')
handleOrderCreated(@Payload() data: { orderId: string }) {
// Same code as before. Throws here will trigger JetStream retries.
}

@MessagePattern('order.get')
getOrder(@Payload() data: { id: string }) {
return { id: data.id, status: 'shipped' };
}
}

Step 4 — Replace client injection

Before:

import { ClientProxy } from '@nestjs/microservices';

constructor(@Inject('NATS_SERVICE') private readonly client: ClientProxy) {}

After:

import { ClientProxy } from '@nestjs/microservices';
import { getClientToken } from '@horizon-republic/nestjs-jetstream';

constructor(@Inject(getClientToken('payments')) private readonly client: ClientProxy) {}

client.emit() and client.send() keep their existing signatures.

Step 5 — Adjust for acknowledgment semantics

JetStream is at-least-once. A message may be redelivered after a handler throws or a pod restarts mid-execution. Make handlers idempotent:

  • Use a unique identifier from the payload (e.g., orderId) to deduplicate.
  • For commands that produce side effects (charge a card, send an email), check whether the side effect already happened before doing it again.
  • See Idempotency in the events pattern for concrete techniques.

What you gain

After migration, you get these capabilities for free:

See also