· 2 min read · WebSockets, Real-time, Node.js, Flutter, APIs
WebSockets, polling or server-sent events for live updates
Three ways to push live updates to a web or mobile app: short polling, server-sent events and WebSockets. How each one works, what it costs, and when I use each.
Order status, chat messages, dashboards, notifications: sooner or later an app needs to show changes without the user refreshing. On Bahria Food Street, the website, the Flutter app and the admin panel share the same REST APIs, with WebSockets for live updates. WebSockets aren't always the right answer, though. There are three main options, and the simplest one that works is usually the best.
1. Polling: ask again every few seconds
The client calls a normal endpoint on a timer.
useEffect(() => {
const id = setInterval(async () => {
const res = await fetch(`/api/orders/${orderId}/status`);
setStatus((await res.json()).status);
}, 5000);
return () => clearInterval(id);
}, [orderId]);- Good: works everywhere, needs no special server, easy to cache and debug.
- Bad: updates arrive up to one interval late, and most requests return "nothing changed". With thousands of open screens, that adds up.
- Use it when: a few seconds of delay is fine and the number of users is modest. An admin report that refreshes every 30 seconds doesn't need anything more.
2. Server-sent events: the server talks, the client listens
Server-sent events (SSE) keep one HTTP response open, and the server writes events into it as they happen. The browser's built-in EventSource handles the connection and reconnects on its own.
const source = new EventSource("/api/notifications/stream");
source.addEventListener("notification", (e) => {
showToast(JSON.parse(e.data));
});- Good: plain HTTP, so it passes through proxies and CDNs easily. Automatic reconnection. Simple to write on the server.
- Bad: one direction only, from server to client. Text only.
- Use it when: the server pushes and the client mostly reads, like notifications, live feeds, progress bars for background jobs, or streaming AI responses.
3. WebSockets: a two-way connection
A WebSocket upgrades an HTTP connection into a persistent, two-way channel. Either side can send a message at any time with very little overhead.
const socket = new WebSocket("wss://api.example.com/live");
socket.onopen = () => socket.send(JSON.stringify({ type: "subscribe", order: orderId }));
socket.onmessage = (e) => {
const msg = JSON.parse(e.data);
if (msg.type === "order.updated") setStatus(msg.status);
};- Good: the lowest latency, both directions, works the same from a browser and a Flutter app.
- Bad: you have to handle reconnection, authentication, heartbeats and scaling yourself, or use a library or managed service that does.
- Use it when: many clients need instant updates, or clients also send frequent messages: chat, live order tracking, collaborative editing, multiplayer features.
Lessons from running WebSockets in production
- Reconnect with backoff. Mobile networks drop constantly. Reconnect automatically, waiting a little longer after each failure so a server restart isn't met by every client at once.
- Refetch after reconnecting. Messages sent while a client was offline are lost. After a reconnect, load the current state from the REST API, then carry on listening.
- Authenticate the connection. Check a token when the socket opens, and check permissions when a client subscribes to a channel, not just once.
- Send heartbeats. Proxies and load balancers close connections that look idle. A ping every 25 to 30 seconds keeps them open and detects dead clients.
- Keep REST as the source of truth. The socket says "something changed"; the API holds the real data. That keeps the app correct even when a message is missed.
How I choose
- A delay of a few seconds is fine: polling
- The server pushes, the client listens: server-sent events
- Instant, two-way, or many users at once: WebSockets
Building an app that needs live updates on web and mobile? Tell me about it.