Real-Time: WebSockets, SSE & Long Polling

intermediate websockets sse long-polling real-time http

Here’s the core problem. Plain HTTP is request-response — the client asks, the server answers, done. The server can’t just tap the client on the shoulder and say “hey, new message arrived”. So how do we build a live chat, a stock ticker, or a notifications badge?

In simple language — we need a way for the server to push data to the client instead of waiting to be asked. There are four common approaches, each a step up in real-time-ness.

Short polling — “are we there yet?”

The dumbest-but-simplest option. The client asks on a timer: every few seconds it fires a request, “anything new?” Server answers immediately, usually “nope”.

It works, but it’s wasteful. Most requests come back empty, and there’s always lag — a message that lands right after a poll waits until the next one. Lots of traffic, little payoff.

// short polling — hammer the server every 3s
setInterval(async () => {
  const res = await fetch("/api/messages?since=" + lastId);
  const msgs = await res.json();
  render(msgs); // mostly empty responses
}, 3000);

Long polling — “hold the line”

Smarter. The client asks, but the server holds the request open until it actually has something to say (or a timeout hits). The moment there’s data, it responds. The client immediately opens a new request and waits again.

Same request-response model, but way fewer empty responses and near-instant delivery. It’s how a lot of “real-time” was done before WebSockets existed, and it’s still a solid fallback.

// long polling — server holds the connection until there's data
async function poll() {
  const res = await fetch("/api/updates"); // may hang for ~30s
  render(await res.json());
  poll(); // immediately re-open
}
poll();

SSE — a one-way stream from the server

Server-Sent Events open a single long-lived HTTP connection over which the server streams messages one way: server → client. The client can’t send data back on that channel (it uses normal HTTP requests for that).

It’s built into browsers via EventSource, auto-reconnects for us, and rides over regular HTTP — so proxies and firewalls don’t complain. Perfect for feeds, notifications, live scores, progress bars.

// SSE — the browser handles reconnection for us
const es = new EventSource("/api/stream");
es.onmessage = (e) => render(JSON.parse(e.data));
// one-way: server pushes, we just listen

WebSockets — a two-way pipe

WebSockets give us a full-duplex connection — both sides can send messages any time, independently. In simple language: a real two-way phone call instead of walkie-talkie turns.

It starts life as an HTTP request that gets upgraded to the WebSocket protocol (ws:// or wss://), then stays open as a persistent TCP connection. Once upgraded, it’s no longer HTTP — just a raw message pipe both ways with tiny per-message overhead.

const ws = new WebSocket("wss://api.chat.com/room/42");
ws.onopen = () => ws.send("hello");     // client -> server
ws.onmessage = (e) => render(e.data);   // server -> client, any time

The upgrade handshake

WebSockets sneak in through a normal HTTP request so they work everywhere HTTP works.

GET /room/42 HTTP/1.1
Host: api.chat.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

The server agrees with a 101 Switching Protocols, and from that point the same TCP connection carries WebSocket frames instead of HTTP.

Which one, when?

Here’s the whole picture — direction of data flow tells the story.

Short polling client → server, on a timer
C —ask→ S · C —ask→ S · C —ask→ S   (mostly empty · laggy · wasteful)
Long polling client → server, server holds it
C —ask→ S …holds… S —reply→ C · repeat   (near real-time · simple fallback)
SSE server → client, one-way stream
S —push→ C —push→ C —push→ C   (feeds · notifications · auto-reconnect)
WebSocket client ⇄ server, full-duplex
C ⇄ S ⇄ C ⇄ S   (chat · multiplayer · live collaboration)

Rules of thumb:

  • Only the server needs to push, one-way?SSE. Simpler than WebSockets, rides plain HTTP, free reconnection.
  • Both sides chatter constantly (chat, games, collab editing)?WebSockets.
  • Can’t use either / need a dead-simple fallback?long polling.
  • Short polling? Only for low-stakes stuff where a few seconds of lag is fine and you want zero moving parts.

Interview soundbite

HTTP is request-response, so the server can’t push on its own. Short polling asks on a timer (wasteful, laggy). Long polling holds the request open until there’s data (near real-time, good fallback). SSE is a one-way server→client stream over plain HTTP with auto-reconnect — great for feeds and notifications. WebSockets upgrade an HTTP connection (via Upgrade header → 101 Switching Protocols) into a persistent full-duplex pipe — use them when both sides need to send freely, like chat or multiplayer.