DEV Community

Cover image for Real-Time Data Without WebSocket Overhead
Athreya aka Maneshwar
Athreya aka Maneshwar

Posted on

Real-Time Data Without WebSocket Overhead

Hi there! I'm Maneshwar. Right now, I’m building LiveAPI, a first-of-its-kind tool for helping you automatically index API endpoints across all your repositories. LiveAPI helps you discover, understand, and use APIs in large tech infrastructures with ease.


If you've ever built something like a live score ticker, stock updates, or notification feed, chances are you've needed real-time updates from your server.

While WebSockets often steal the spotlight, there's a quieter but super-effective player on the field: Server-Sent Events (SSE).

Let's break it down and show you how to implement it from scratch using just HTML + JavaScript + a simple backend.

What Are Server-Sent Events?

Server-Sent Events (SSE) enable one-way, real-time communication from the server to the browser, using a single long-lived HTTP connection.

Key Features:

  • Unidirectional (server ➡️ client)
  • Reconnects automatically
  • Lightweight compared to WebSockets
  • Text-based event format
  • Native browser support via the EventSource API

When Should You Use SSE?

Great for:

  • Live dashboards
  • Notifications
  • News or social feeds
  • Streaming logs or metrics

Not ideal for:

  • Chat apps (need bidirectional)
  • Multiplayer games
  • Anything requiring client-to-server messages

How It Works: The Basics

  1. Client opens a connection using EventSource.
  2. Server keeps the connection open and streams updates in text/event-stream format.
  3. Client listens for messages using .onmessage or addEventListener.

Let's Build It

Backend (Node.js Example)

// sse-server.js
const http = require('http');

http.createServer((req, res) => {
  if (req.url === '/events') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    });

    const interval = setInterval(() => {
      const now = new Date().toLocaleTimeString();
      res.write(`data: Server time is ${now}\n\n`);
    }, 2000);

    req.on('close', () => {
      clearInterval(interval);
      res.end();
    });
  } else {
    res.writeHead(404);
    res.end();
  }
}).listen(3000, () => {
  console.log('SSE server running on http://localhost:3000');
});
Enter fullscreen mode Exit fullscreen mode

Frontend (HTML + JS)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>SSE Example</title>
</head>
<body>
  <h1>🛰️ Real-Time Server Time</h1>
  <div id="output">Waiting for updates...</div>

  <script>
    const output = document.getElementById('output');

    const evtSource = new EventSource("http://localhost:3000/events");

    evtSource.onopen = () => {
      console.log("Connection opened");
    };

    evtSource.onmessage = (event) => {
      output.innerText = event.data;
    };

    evtSource.onerror = (err) => {
      console.error("EventSource failed:", err);
    };
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Server-Sent Format

Every SSE message looks like this:

data: Hello World

data: Another message

Enter fullscreen mode Exit fullscreen mode

Optional fields:

// Named event
event: custom
data: Custom message

// Retry interval in milliseconds
retry: 5000
Enter fullscreen mode Exit fullscreen mode

Note: A blank line means “end of message.”

EventSource API Breakdown

Property / Method Description
EventSource(url) Opens the connection
onmessage Default handler for incoming data
addEventListener("foo") Listen for custom event types
close() Manually close the connection
readyState 0 = connecting, 1 = open, 2 = closed
withCredentials Handles CORS cookies

Gotchas & Tips

  • Max connections: Browsers often limit 6 connections per domain (per tab), unless you’re using HTTP/2.
  • CORS: If your frontend is on a different domain, the server must send:
  Access-Control-Allow-Origin: http://your-frontend.com
Enter fullscreen mode Exit fullscreen mode
  • Auto-reconnect: Happens automatically unless you call .close().
  • No binary support: SSE only supports UTF-8 text.

SSE vs WebSockets

Feature SSE WebSocket
Direction One-way Bi-directional
Protocol HTTP WS/TCP
Built-in reconnect Yes No
Text only Yes Text + Binary
CORS support Easy Requires config
Complexity Low Medium-High

Conclusion

SSE is the perfect lightweight tool when your use case is read-only from server to client, like dashboards, feeds, or real-time monitoring.

No need for a full WebSocket setup or third-party libraries—just native browser support, some headers, and a res.write().

Bonus: Named Events

// Server
res.write('event: alert\n');
res.write('data: You got a new message!\n\n');
Enter fullscreen mode Exit fullscreen mode
// Client
evtSource.addEventListener("alert", (e) => {
  alert(e.data);
});
Enter fullscreen mode Exit fullscreen mode

Simple and sweet.

Let SSE handle the push, and you just sip that data like a chilled stream. ☕


LiveAPI helps you get all your backend APIs documented in a few minutes

With LiveAPI, you can quickly generate interactive API documentation that allows users to search and execute APIs directly from the browser.

Image description

If you’re tired of manually creating docs for your APIs, this tool might just make your life easier.

Top comments (1)

Collapse
 
kiponos profile image
Devops Kiponos

Very nice. Good Luck with LiveAPI !!

How do you generate the docs pages?

Btw, WebSockets are very complex, and that's why in Kiponos were providing an SDK that simplify and hides all the WebSockets complexity so you can benefit of bi-directional, permanent connection with reconnect strategies all in a simple SDK.

If you have a Java server like Spring Boot, check us out: Kiponos.io.

Kiponos is a true real-time configuration hub. Whatever you modify online, is instantly affecting your app.