Skip to content

NATS

NatsActor integrates with NATS — the lightweight pub/sub broker. Core NATS is fire-and-forget without durability; for durable streams, see the JetStream variant.

import { ActorSystem, Props, NatsActor } from 'actor-ts';
const nats = system.actorOf(
Props.create(() => new NatsActor({
servers: ['nats://nats-1:4222', 'nats://nats-2:4222'],
name: 'my-app',
})),
'nats',
);
// Subscribe (wildcards supported):
nats.tell({
kind: 'subscribe-nats',
subject: 'events.>',
subscriber: eventHandler,
});
// Publish:
nats.tell({
kind: 'publish',
subject: 'events.user.signup',
payload: JSON.stringify(event),
});
interface NatsActorSettings extends BrokerCommonSettings {
servers: string[] | string;
name?: string; // client identifier
user?: string;
pass?: string;
token?: string;
tls?: TlsOptions;
pingInterval?: number; // default 120s
}

NATS uses .-delimited subjects:

events.user.signup
events.user.delete
orders.priority.placed
metrics.gauge.cpu

Subjects are case-sensitive; per convention they’re lowercase + dot-separated.

WildcardMatches
*Exactly one token (matches user in events.user.signup).
>One or more tokens (matches user.signup in events.user.signup).
'events.>' → events.user.signup, events.user.delete, events.order.placed
'events.*.signup' → events.user.signup, events.admin.signup

> can only appear as the last token.

NATS supports synchronous request-reply natively:

nats.tell({
kind: 'request',
subject: 'account.balance',
payload: JSON.stringify({ accountId: '42' }),
timeoutMs: 1_000,
replyTo: this.self, // gets the reply message
});
// Reply handler:
override onReceive(msg: NatsRequestReply): void {
if (msg.kind === 'reply') this.handleBalance(JSON.parse(...));
if (msg.kind === 'timeout') this.handleTimeout();
}

This is fast — a few hundred microseconds round-trip on localhost. Used widely as the “RPC over NATS” pattern.

Three primary use cases:

  1. High-throughput pub/sub without the operational complexity of Kafka.
  2. Microservices request/reply — synchronous calls between services via subjects.
  3. Lightweight event distribution — fire-and-forget notifications, metrics, log streams.

For durable streams (replay, history, ack semantics), see JetStream below. For cluster-internal pub/sub, DistributedPubSub is simpler.

For NATS with durability, use JetStreamActor:

import { JetStreamActor } from 'actor-ts';
const js = system.actorOf(
Props.create(() => new JetStreamActor({
servers: ['nats://nats-1:4222'],
streams: [
{
name: 'ORDERS',
subjects: ['orders.>'],
storage: 'file',
maxAge: 86_400 * 7, // 7 days retention
},
],
})),
'js',
);

JetStream layers:

  • Stream — durable log, subjects → records.
  • Consumer — read cursor; multiple consumers can read the same stream independently.
  • Ack semantics — like AMQP, consumers ack each message.

Use JetStream when you need:

  • Replay — consumers can rewind to any offset.
  • Persistence — survives broker restart (file-backed storage).
  • Ordered consumption per subject.

Kafka still beats JetStream for massive scale (billions of events / sec across hundreds of partitions). JetStream is the sweet spot for “less than Kafka, more than core NATS.”

Terminal window
npm install nats
# or: bun add nats

The nats package includes both core NATS and JetStream clients.