Back to Spy NATS
Spy NATS Blog/ Restaurant Kitchen Order Routing with NATS Queue Groups

Restaurant Kitchen Order Routing with NATS Queue Groups

A hospitality use case: routing incoming orders to the right kitchen station (grill, salad, dessert) using subject-based routing combined with queue groups so each station's cooks share the load evenly.

4 min read
  • nats
  • hospitality
  • queue-groups
  • routing
  • tutorial

The scenario

A restaurant's point-of-sale system needs to route each order line to the correct kitchen station — grill, salad, dessert — and within a busy station, spread tickets across however many cooks are working that station right now.

Step 1 — subject-per-station routing

terminal 1
docker run -d --name nats-server -p 4222:4222 -p 8222:8222 nats:latest
nats context add local --server="nats://localhost:4222" --select
terminal 2 — grill cook 1
nats sub "kitchen.grill.ticket" --queue grill-station
terminal 3 — salad cook
nats sub "kitchen.salad.ticket" --queue salad-station
terminal 4 — the POS sends a multi-item order as separate station tickets
nats pub kitchen.grill.ticket '{"orderId":501,"item":"ribeye, medium-rare"}'
nats pub kitchen.salad.ticket '{"orderId":501,"item":"caesar salad, no croutons"}'

Each ticket goes only to the station it's addressed to — the grill subscriber never sees the salad ticket, because they're different subjects entirely, not just different queue groups on one subject.

Step 2 — a busy grill station: multiple cooks share the load

terminal 5 — a second grill cook joins during the dinner rush
nats sub "kitchen.grill.ticket" --queue grill-station
terminal 4 — a burst of grill tickets
for i in $(seq 1 6); do nats pub kitchen.grill.ticket "{\"orderId\":$((500+i)),\"item\":\"burger #$i\"}"; done

The 6 tickets alternate between the two grill cooks (terminals 2 and 5) — one order per cook at a time, never duplicated — while the salad station (terminal 3), still just one cook, receives none of this burst since it was published to a different subject.

Step 3 — an expo/dashboard view across every station at once

The head chef's expo screen needs to see everything, from every station, without joining any single station's workload:

terminal 6 — expo screen: every ticket, every station, NOT in the queue group
nats sub "kitchen.*.ticket"

This subscriber deliberately omits --queue — it's a plain wildcard subscription, so it receives a copy of every ticket in addition to whichever station cook also receives it for cooking, rather than competing for tickets.

Step 4 — a station temporarily has zero cooks

terminal 4 — publish to dessert station with no cook subscribed
nats pub kitchen.dessert.ticket '{"orderId":505,"item":"tiramisu"}'

With plain pub/sub (as used above), this ticket is silently dropped if no dessert cook is currently subscribed — acceptable for a live "cook now" ticket during service, but risky if the restaurant needs a guarantee that no ticket is ever lost even during a brief station gap. For that stronger guarantee, use the same JetStream-backed durable pull consumer pattern shown in the e-commerce fulfillment article, one stream per station.

Troubleshooting

SymptomLikely causeFix
Grill ticket shows up on the salad station's screenSubject typo (e.g. kitchen.grll.ticket) at publish timeCentralize the station-subject mapping in the POS code so it can't drift from what cooks subscribe to
Two grill cooks both cook the same ticketOne of them subscribed without --queue grill-station, making it a plain wildcard/duplicate subscriber instead of a load-balanced oneConfirm every station cook's subscription uses the exact same queue group name
Expo screen misses some ticketsExpo subscriber accidentally used --queue, making it compete with cooks instead of seeing everythingExpo/monitoring subscribers must NOT use --queue — they need a full copy, not a share
A ticket sent to an empty station is lostExpected with plain pub/sub — no cook was subscribed at that instantIf that's unacceptable, back the station's subject with a JetStream stream + durable consumer instead of plain nats sub

Recap

ConceptTakeaway
kitchen.<station>.ticketSubject-per-station gives correct routing with zero central routing logic
--queue <station>-stationLoad-balances tickets across however many cooks are on that station right now
Plain wildcard sub (no --queue)Right choice for a "see everything" dashboard that shouldn't compete for work
Empty station riskPlain pub/sub drops tickets published while no cook is subscribed — use JetStream if that's unacceptable

SpyNATS is an independent tool and is not affiliated with, sponsored by, or endorsed by Synadia Communications or the NATS.io project.