skip to content
szczecin 53.43N 14.55E ./start-project

It's NUTS: a live website with Symfony, NATS and NUTS

(nats, symfony, php) 1 min read by Bartosz Pachołek

A real-time price ticker where NATS is the only backend service: work queue, key-value store and live event bus at once.

It's NUTS: build a dynamic website using Symfony, NATS and NUTS walks through a real-time price ticker, and its code is on GitHub as nut-exchange. The interesting part is what is not there: no database, no Redis and no separate message broker.

NATS plays three roles

role NATS feature used by
work queue INGEST stream Symfony Messenger
last-value store nutprices KV bucket the worker writes, the home page reads
live event bus EVENTS stream NUTS, to the browser

How a price travels

text
POST /api/ticks -> Messenger -> INGEST stream
                                  |
                           NutTickHandler (worker)
                    KV "nutprices"   publish events.prices
                                            |
GET / -> KV snapshot          NUTS -> EventSource('/events?topic=prices')

The page renders the current prices straight from the KV bucket, then subscribes over SSE and applies live changes.

Safe against duplicates

The handler combines a timestamp guard (never overwrite a newer value) with revision-based compare-and-set on the KV bucket, so duplicate or out-of-order deliveries and concurrent writers are safe. The JetStream publish carries a message id for stream-level deduplication.

FrankenPHP embeds PHP in Caddy and NUTS is a Caddy module, so the whole stack is one web process plus one worker.