NATS
NATS is a simple, secure, and high-performance open source messaging system for cloud native applications, IoT messaging, and microservices architectures. The NATS server is written in Go, and client libraries are available for dozens of major programming languages. NATS supports both At Most Once and At Least Once delivery. It runs anywhere, from large servers and cloud instances to edge gateways and Internet of Things devices.
Installation#
To start building NATS-based microservices, first install the required package:
$ npm i --save @nats-io/transport-node
Warning As of NestJS v12, the NATS transporter targets NATS v3 and uses the@nats-io/transport-nodedriver. If you are upgrading from an earlier version, uninstall the legacynatspackage (npm uninstall nats) and install@nats-io/transport-nodeinstead. See the migration guide for details.
Overview#
To use the NATS transporter, pass the following options object to the createMicroservice() method:
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.NATS,
options: {
servers: ['nats://localhost:4222'],
},
});
const app = await NestFactory.createMicroservice(AppModule, {
transport: Transport.NATS,
options: {
servers: ['nats://localhost:4222'],
},
});
Hint TheTransportenum is imported from the@nestjs/microservicespackage.
Options#
The options object is specific to the chosen transporter. The NATS transporter exposes the NATS connection options, as well as the following properties:
queue | Queue group that your server subscribes with (leave undefined to ignore this setting). Read more about NATS queue groups below. |
gracefulShutdown | Enables graceful shutdown. When enabled, the server first unsubscribes from all subjects, then waits for gracePeriod before closing the connection. Default is false. |
gracePeriod | Time in milliseconds to wait after unsubscribing from all subjects when gracefulShutdown is enabled. Default is 10000 ms. |
headers | Headers added to every message a client sends (see record builders). |
Client#
As with other microservice transporters, you have several options for creating a NATS ClientProxy instance.
One way to create an instance is to use the ClientsModule. Import it and use its register() method to pass an options object with the same properties shown above for the createMicroservice() method, plus a name property to use as the injection token. Read more about the ClientsModule in the client section of the overview.
@Module({
imports: [
ClientsModule.register([
{
name: 'MATH_SERVICE',
transport: Transport.NATS,
options: {
servers: ['nats://localhost:4222'],
}
},
]),
]
...
})
You can also create a client with ClientProxyFactory or the @Client() decorator. Both are described in the client section of the overview.
Request-response#
For the request-response message style, the NATS transporter doesn't use the built-in NATS Request-Reply API. Instead, the client publishes a "request" on a given subject with the publish() method and a unique reply subject name. Responders listen on that subject and send responses to the reply subject. Reply subjects are routed back to the requester dynamically, regardless of the location of either party.
Event-based#
For the event-based message style, the NATS transporter uses the built-in NATS Publish-Subscribe mechanism. A publisher sends a message on a subject, and every active subscriber listening on that subject receives the message. Subscribers can also register interest in wildcard subjects. This one-to-many pattern is sometimes called fan-out.
Queue groups#
NATS provides a built-in load balancing feature called distributed queues. To create a queue subscription, use the queue property as follows:
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.NATS,
options: {
servers: ['nats://localhost:4222'],
queue: 'cats_queue',
},
});
Context#
In more complex scenarios, you may need additional information about the incoming request. With the NATS transporter, you can access the NatsContext object.
@MessagePattern('notifications')
getNotifications(@Payload() data: number[], @Ctx() context: NatsContext) {
console.log(`Subject: ${context.getSubject()}`);
}
@Bind(Payload(), Ctx())
@MessagePattern('notifications')
getNotifications(data, context) {
console.log(`Subject: ${context.getSubject()}`);
}
Hint@Payload(),@Ctx()andNatsContextare imported from the@nestjs/microservicespackage.
Wildcards#
A subscription can target an explicit subject, or it can include wildcards.
@MessagePattern('time.us.*')
getDate(@Payload() data: number[], @Ctx() context: NatsContext) {
console.log(`Subject: ${context.getSubject()}`); // e.g. "time.us.east"
return new Date().toLocaleTimeString(...);
}
@Bind(Payload(), Ctx())
@MessagePattern('time.us.*')
getDate(data, context) {
console.log(`Subject: ${context.getSubject()}`); // e.g. "time.us.east"
return new Date().toLocaleTimeString(...);
}
Record builders#
To configure message options, use the NatsRecordBuilder class. It works for both request-response and event-based flows. For example, to add an x-version header, use the setHeaders() method:
import * as nats from '@nats-io/nats-core';
// somewhere in your code
const headers = nats.headers();
headers.set('x-version', '1.0.0');
const record = new NatsRecordBuilder(':cat:').setHeaders(headers).build();
this.client.send('replace-emoji', record).subscribe(...);
Hint TheNatsRecordBuilderclass is exported from the@nestjs/microservicespackage.
On the server side, you can read these headers through the NatsContext:
@MessagePattern('replace-emoji')
replaceEmoji(@Payload() data: string, @Ctx() context: NatsContext): string {
const headers = context.getHeaders();
return headers?.get('x-version') === '1.0.0' ? '🐱' : '🐈';
}
@Bind(Payload(), Ctx())
@MessagePattern('replace-emoji')
replaceEmoji(data, context) {
const headers = context.getHeaders();
return headers?.get('x-version') === '1.0.0' ? '🐱' : '🐈';
}
To configure headers for all requests sent by a client, pass them as options to the ClientProxyFactory:
import { Module } from '@nestjs/common';
import { ClientProxyFactory, Transport } from '@nestjs/microservices';
@Module({
providers: [
{
provide: 'API_v1',
useFactory: () =>
ClientProxyFactory.create({
transport: Transport.NATS,
options: {
servers: ['nats://localhost:4222'],
headers: { 'x-version': '1.0.0' },
},
}),
},
],
})
export class ApiModule {}
Headers set on a record take precedence over the client-level headers option.
Starting with NestJS v12.1.3, a message handler can also return a record. Nest sends the record data as the reply and the record headers as the headers of the reply message:
import * as nats from '@nats-io/nats-core';
@MessagePattern('replace-emoji')
replaceEmoji() {
const headers = nats.headers();
headers.set('x-version', '1.0.0');
return new NatsRecordBuilder(':cat:').setHeaders(headers).build();
}
Custom serializers and deserializers#
Starting with NestJS v12, Nest serializes NATS packets as JSON strings, and custom NATS deserializers receive the full NATS message object instead of a raw Uint8Array. If you have written a custom deserializer, read the payload through msg.json() rather than decoding bytes manually:
import { Deserializer, IncomingRequest } from '@nestjs/microservices';
export class CustomNatsDeserializer implements Deserializer {
deserialize(msg: any): IncomingRequest {
// Previously: JSON.parse(new TextDecoder().decode(msg));
return msg.json();
}
}
Instance status updates#
To get real-time updates on the connection and the state of the underlying driver instance, subscribe to the status stream. This stream provides status updates specific to the chosen driver. For the NATS driver, the status stream emits connected, disconnected, and reconnecting events.
this.client.status.subscribe((status: NatsStatus) => {
console.log(status);
});
Hint TheNatsStatustype is imported from the@nestjs/microservicespackage.
Similarly, you can subscribe to the server's status stream to receive notifications about the server's status.
const server = app.connectMicroservice<MicroserviceOptions>(...);
server.status.subscribe((status: NatsStatus) => {
console.log(status);
});
Listening to NATS events#
In some cases, you might want to listen to internal events emitted by the microservice. The NATS transporter emits the disconnect, reconnect, and update (cluster servers added or removed) events. For example, you could listen for the disconnect event to trigger additional operations when the connection is lost. To do this, use the on() method, as shown below:
this.client.on('disconnect', (serverUrl) => {
console.error(`Disconnected from ${serverUrl}`);
});
Similarly, you can listen to the server's internal events:
server.on<NatsEvents>('disconnect', (serverUrl) => {
console.error(`Disconnected from ${serverUrl}`);
});
Hint TheNatsEventstype is imported from the@nestjs/microservicespackage.
Underlying driver access#
For more advanced use cases, you may need to access the underlying driver instance, for example, to close the connection manually or to use driver-specific methods. In most cases, however, you shouldn't need to access the driver directly.
To do so, use the unwrap() method, which returns the underlying driver instance. The generic type parameter specifies the type of driver instance you expect.
const natsConnection =
this.client.unwrap<import('@nats-io/transport-node').NatsConnection>();
Similarly, you can access the server's underlying driver instance:
const natsConnection =
server.unwrap<import('@nats-io/transport-node').NatsConnection>();

