Real-Time APIs (Polling & SSE)

HTTP was originally designed as a request-response protocol: the client asks for data, the server responds, and the connection closes.

But what if you are building a live sports ticker, a stock trading dashboard, or a chat application? The server needs a way to push data to the client when it happens.

Here is the evolution of how engineers hacked HTTP to achieve real-time communication.

1. Short Polling (The Naive Approach)

The easiest way to get live updates is simply to ask the server over and over again.

[!TIP] ELI5: Are We There Yet? Imagine driving with a kid in the back seat.

  • Kid: "Are we there yet?"
  • Parent: "No."
  • (10 seconds later) Kid: "Are we there yet?"
  • Parent: "No."

The client runs a JavaScript setInterval loop, firing a GET request every 5 seconds.

2. Long Polling

To fix the wasted requests of Short Polling, engineers invented Long Polling.

Instead of immediately responding with "No new data," the server intentionally holds the HTTP connection open for up to 30 seconds.

  1. Client requests data.
  2. Server sees there is no data yet. It parks the request and waits.
  3. (15 seconds later) A new message arrives in the database.
  4. The Server immediately responds to the parked request with the message.
  5. The Client receives the message and immediately opens a new Long Poll connection.

3. Server-Sent Events (SSE)

Long polling is still a hack. If you just need the server to push text data to the client, the modern, elegant solution is Server-Sent Events (SSE).

SSE leverages a standard HTTP connection but keeps it open indefinitely. The server streams data down the pipe whenever it wants, utilizing the text/event-stream content type.

The Implementation

Frontend (Native Browser API):

const eventSource = new EventSource('/api/live-scores');

eventSource.onmessage = function(event) {
  const newScore = JSON.parse(event.data);
  console.log("Goal Scored!", newScore);
};

Backend (Node.js):

app.get('/api/live-scores', (req, res) => {
  // Keep the connection alive and set the correct headers
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  // When a goal happens, write it directly to the stream!
  sportsEngine.on('goal', (data) => {
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  });
});