Real-Time & WebSockets
Sometimes the UI needs live updates: a case status changing, a notification, a dashboard updating. Real-time connections (WebSockets, SignalR, server-sent events) make this possible. But a long-lived open connection is very different from a request/response call. It needs its own approach to auth, scale, security, and reconnection.
A normal API call is short: authenticate, do the work, respond. A real-time connection stays open and pushes data as things happen. This brings new questions. How do you authenticate and keep authorising over a long-lived connection? What happens when it drops (it will)? How does it scale across multiple server instances? And how do you avoid leaking data to the wrong connection? Use real-time where it genuinely helps, and apply the same security care as everywhere else.
Do not reach for it by default. Polling or normal requests are simpler and often enough. Real-time is worth its complexity when updates are frequent and latency matters.
Secure the connection
- AlwaysAuthenticate and authorise real-time connections like any other access. Establish identity at connect time, and re-check authorization for what each message or subscription can see (see Authentication & Authorization).
- DoScope every message to the right user or tenant. A subscriber must receive only their own data. Never broadcast one tenant's events to another tenant's connection (see Multi-Tenancy).
- DoUse secure transport (wss/TLS), validate inbound messages as untrusted input, and apply rate and size limits to messages (see Trust Boundaries, Rate Limiting & Abuse Prevention).
- DoHandle token expiry on long-lived connections. Re-validate or refresh so a connection does not outlive the user's authorization.
- NeverSend data to a user on a real-time channel without confirming that connection is authorised for it. Open connections are an easy place to leak data across users or tenants.
Build for scale and failure
- DoPlan for connections dropping. Clients reconnect with backoff, and the design tolerates missed messages. Re-sync state on reconnect instead of assuming perfect delivery (see Designing for Failure).
- DoMake it scale across instances. Use a backplane (for example, Redis or Azure SignalR) so messages reach a user on whichever instance holds their connection. Do not keep critical state only in one instance's memory (see Concurrency).
- DoMind resource use. Connections consume memory and sockets, so bound them and clean up on disconnect (see Performance & Resource Use).
- ConsiderWhether you actually need real-time. Simple polling or normal requests are easier to build and operate, and fine for infrequent updates.
- AvoidTreating real-time delivery as guaranteed or ordered. Design it as you would for messaging, so clients can recover state (see Asynchronous Messaging).
Self-review checklist
- AskIs the connection authenticated, and is each message scoped to the right user/tenant?
- AskWhat happens when the connection drops — does the client reconnect and re-sync safely?
- AskDoes this work across multiple server instances, not just in memory on one?
- AskDo I actually need real-time, or would polling do?