Skip to main content
Back to DevlogsEngineering field notes
Real-TimeSSEReliabilityKDS

Why the Kitchen Screen Uses SSE

A realtime pipeline designed for unreliable restaurant networks, one-way order flow, automatic recovery, and zero ghost listeners.

Published on September 5, 2026
~2 min read

Realtime Is an Operational Promise

In a kitchen display system, “eventually” has a physical cost. A missed order is not a stale dashboard metric; it is a guest waiting at a table while the kitchen sees nothing. Sofreh's KDS begins with server-rendered active orders, then opens a live stream for new orders and status changes. The initial snapshot prevents an empty screen after refresh, while the stream keeps it current.

The product requirement is asymmetric. The kitchen mostly receives events; its state-changing actions still use ordinary authenticated requests. That one-way shape is important because it removes the strongest argument for a bidirectional socket.

Plain HTTP Was the Feature

Server-Sent Events travel over a long-lived HTTP response, reconnect naturally through EventSource, and behave well behind the inexpensive routers and reverse proxies common in restaurants. WebSockets would have added an upgrade path, another protocol to monitor, and bidirectional complexity the KDS did not need.

Sofreh's shared client hook owns connection state and exponential-backoff reconnection. The KDS registers named handlers such as NEW_ORDER, deduplicates incoming orders by ID, and updates the local stage board. When sound preferences change, handlers are replaced rather than stacked, preventing one event from producing multiple chimes after repeated renders.

A Disconnected Screen Must Truly Leave

The server keeps an in-process channel per establishment. Each channel contains the listeners currently interested in that restaurant's events. Broadcasting formats the event once, sends it to every listener, and collects failures instead of letting one dead connection interrupt the rest.

Failed listeners are removed and closed; an empty channel is deleted entirely. Request aborts follow the same cleanup path. This small lifecycle is what separates a demo from a service that can run for weeks: without it, every closed browser leaves a ghost behind, broadcasts get progressively slower, and memory growth appears unrelated to traffic.

Keep Tomorrow's Scale Behind One Seam

An in-process channel is the fastest correct choice for a single application worker: no serialization, no network hop, and no Redis dependency on the hot path. It would be the wrong choice for multiple independent workers because listeners would be divided across processes.

The answer was not to pay the distributed-systems tax early. It was to isolate broadcasting behind a realtime service so the channel map has a clear migration target. If horizontal scale becomes necessary, Redis pub/sub can replace internal fan-out without rewriting the KDS or every order workflow. Good architecture does not predict every future—it makes the next justified change local.