Realtime Apps & WebSockets Guide
Realtime is a spectrum: polling every thirty seconds, server-sent events streaming one way, WebSockets talking both ways. Matching the transport to the feature matters more than any framework choice.
The hard question on modern clouds is where a long-lived connection actually lives, because serverless functions on Lambda or Vercel terminate and cannot hold sockets open. You either run a stateful server, use Durable Objects, or buy a managed realtime service.
SSE vs WebSockets vs polling
Server-sent events run over plain HTTP, reconnect automatically, and are perfect for one-way flows — notifications, dashboards, LLM token streams. WebSockets earn their complexity when the client talks back constantly: chat, collaborative cursors, multiplayer state. Simple polling remains respectable for data that changes every minute or two; it caches well and never breaks through proxies. Start with the dumbest transport that meets your latency needs.
Where the connection lives
Three workable homes for sockets:
- A stateful Node server — Socket.IO on Render, Railway or Fly.io, scaled out with the Redis adapter and sticky sessions
- Cloudflare Durable Objects — one object per room, with the WebSocket Hibernation API keeping idle connections cheap to hold; PartyKit (now part of Cloudflare) wraps this pattern nicely
- AWS API Gateway WebSocket APIs — Lambda handlers with connection IDs stored in DynamoDB
Buying instead: managed realtime
Pusher Channels and Ably sell pub/sub channels, presence and message history over global infrastructure. Supabase Realtime streams Postgres changes and broadcast messages if you already run on Supabase, and Firebase covers mobile-first apps. Buy when presence, history and multi-region fan-out would otherwise become your roadmap instead of your product.
Details that keep it stable
Authenticate at the handshake with a short-lived signed token, not a long-lived API key. Send heartbeats and reconnect with exponential backoff plus jitter, resuming from a cursor so missed messages replay. Make handlers idempotent — reconnects duplicate messages — and fan out across nodes with Redis pub/sub rather than in-process loops.
Frequently asked questions
Can Vercel or Lambda functions host WebSockets?
Not directly — functions end when the response does. Use a managed realtime service, API Gateway WebSocket APIs, Durable Objects, or a small stateful container.
Is SSE enough for notifications?
Usually yes. It is one-way, HTTP-friendly and auto-reconnecting. Move to WebSockets only when clients must send frequent messages upstream.
How do I scale Socket.IO past one server?
Add the Redis adapter so events broadcast across nodes, and enable sticky sessions at the load balancer so each client keeps hitting the same instance.
How should WebSocket connections authenticate?
Pass a short-lived signed token during the handshake, verify it server-side, and close idle or expired connections, re-authenticating on reconnect.