Bridging NATS to the browser with one Caddy directive
(nats, go, open-source) 1 min read by Bartosz Pachołek
How NUTS turns NATS JetStream subjects into Server-Sent Events, and why a Caddy module was the right home for it.
Browsers already speak one long-lived protocol that needs no library: Server-Sent Events. NATS is great at moving messages between services. NUTS connects the two inside the web server you may already run, as a Caddy module.
One directive
:8080 {
route /events* {
uri strip_prefix /events
nuts {
nats_url nats://nats:4222
stream_name EVENTS
topic_prefix events.
}
}
}
A browser subscribes with the standard API, to one topic or several:
const es = new EventSource('/events?topic=orders&topic=payments');
es.addEventListener('message', (e) => console.log(JSON.parse(e.data)));
Why JetStream
Messages are read from a JetStream stream, so retention and storage stay in NATS and every event has a sequence number. That number is the SSE id; when a browser reconnects it sends Last-Event-ID, and NUTS replays what it missed. Slow clients are disconnected cleanly so they can reconnect and replay.
Who may listen
Subscriber auth uses HMAC-signed JWTs with a subscribe claim listing the topics a client may stream, read from a header or, for EventSource, from a cookie. Prometheus metrics and TLS or mTLS to NATS are built in.
Where it runs
This site uses it: new contact messages and content changes reach the admin panel live through NUTS. Build it with xcaddy build --with github.com/ideaconnect/nuts or run the idcttech/nuts Docker image.