Alex Spinov Kafka needs ZooKeeper and a team to manage it. RabbitMQ needs Erlang expertise. NATS is a single 20MB...
Kafka needs ZooKeeper and a team to manage it. RabbitMQ needs Erlang expertise. NATS is a single 20MB binary that handles pub/sub, queues, and streaming — with zero dependencies.
| Kafka | RabbitMQ | Redis Pub/Sub | NATS | |
|---|---|---|---|---|
| Binary size | 300MB+ | 100MB+ | 10MB | 20MB |
| Dependencies | JVM, ZooKeeper | Erlang | None | None |
| Setup time | Hours | 30 min | 5 min | 30 sec |
| Persistence | Yes | Yes | No | Yes (JetStream) |
| Operations | Complex | Medium | Simple | Simple |
| Latency | ms | ms | sub-ms | sub-ms |
# Docker
docker run -p 4222:4222 nats:latest -js
# -js enables JetStream (persistence)
# Or binary
curl -L https://github.com/nats-io/nats-server/releases/latest/download/nats-server-linux-amd64.zip -o nats.zip
unzip nats.zip && ./nats-server -js
import { connect } from "nats";
const nc = await connect({ servers: "localhost:4222" });
// Subscribe
const sub = nc.subscribe("orders.created");
for await (const msg of sub) {
const order = JSON.parse(new TextDecoder().decode(msg.data));
console.log(`New order: ${order.id}`);
}
// Publish
nc.publish("orders.created", JSON.stringify({ id: "123", total: 99.99 }));
Messages are fire-and-forget. No persistence. Sub-millisecond latency.
// Service (responder)
const sub = nc.subscribe("user.lookup");
for await (const msg of sub) {
const { userId } = JSON.parse(msg.data);
const user = await db.findUser(userId);
msg.respond(JSON.stringify(user));
}
// Client (requester)
const response = await nc.request("user.lookup", JSON.stringify({ userId: "123" }));
const user = JSON.parse(response.data);
Built-in service discovery via request/reply. No service registry needed.
import { connect, AckPolicy, DeliverPolicy } from "nats";
const nc = await connect({ servers: "localhost:4222" });
const js = nc.jetstream();
const jsm = await nc.jetstreamManager();
// Create stream
await jsm.streams.add({
name: "ORDERS",
subjects: ["orders.>"],
retention: "limits",
max_msgs: 1000000,
max_age: 86400000000000, // 24 hours in nanoseconds
});
// Publish (persisted)
await js.publish("orders.created", JSON.stringify({ id: "456" }));
// Consume
const consumer = await js.consumers.get("ORDERS", "my-consumer");
const messages = await consumer.consume();
for await (const msg of messages) {
console.log(JSON.parse(msg.data));
msg.ack();
}
JetStream = Kafka-like streaming without Kafka-like complexity.
const kv = await js.views.kv("config");
await kv.put("feature.dark-mode", "true");
const entry = await kv.get("feature.dark-mode");
console.log(entry?.string()); // "true"
// Watch for changes
const watch = await kv.watch();
for await (const entry of watch) {
console.log(`${entry.key} = ${entry.string()}`);
}
Distributed key-value store — like etcd but built into NATS.
const os = await js.views.os("files");
// Store file
await os.put({ name: "report.pdf" }, readFileSync("report.pdf"));
// Retrieve
const result = await os.get("report.pdf");
Store large objects across NATS clusters.
Need messaging infrastructure? I build distributed systems and data tools. Email spinov001@gmail.com or check my Apify tools.