DEV Community

1xApi
1xApi

Posted on • Edited on • Originally published at 1xapi.com

How to Implement Server-Sent Events (SSE) for Real-Time APIs in Node.js

How to Implement Server-Sent Events (SSE) for Real-Time APIs in Node.js

As of March 2026, real-time communication is a must-have for modern applications. While WebSockets dominate the conversation, Server-Sent Events (SSE) offer a simpler, HTTP-native alternative that's perfect for one-way data streams. In this guide, you'll learn how to implement SSE in Node.js with practical examples.

What Are Server-Sent Events?

Server-Sent Events is a server push technology enabling browsers to receive automatic updates from a server via HTTP. Unlike WebSockets, SSE is unidirectional - the server pushes data to the client, making it ideal for:

  • Live notifications
  • Stock price updates
  • Social media feeds
  • Progress indicators
  • IoT device status

Why choose SSE over WebSockets?

  • Works over standard HTTP/HTTPS
  • Automatic reconnection
  • No custom protocol needed
  • Simpler implementation
  • Works with HTTP/2

Setting Up a Basic SSE Endpoint

Create a new Node.js project and install Express:

mkdir sse-api && cd sse-api
npm init -y
npm install express cors
Enter fullscreen mode Exit fullscreen mode

Here's a basic SSE endpoint in Node.js:

const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());

// SSE endpoint
app.get('/events', (req, res) => {
  // Set headers for SSE
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.flushHeaders();

  // Send initial connection message
  res.write(`data: ${JSON.stringify({ message: 'Connected to SSE stream' })}\n\n`);

  // Simulate real-time updates
  const interval = setInterval(() => {
    const data = {
      timestamp: new Date().toISOString(),
      value: Math.floor(Math.random() * 100)
    };
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  }, 2000);

  // Clean up on client disconnect
  req.on('close', () => {
    clearInterval(interval);
    res.end();
  });
});

app.listen(3000, () => {
  console.log('SSE server running on http://localhost:3000');
});
Enter fullscreen mode Exit fullscreen mode

Consuming SSE in the Browser

Here's how to connect to your SSE endpoint from a web client:

const eventSource = new EventSource('http://localhost:3000/events');

eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);

  // Update your UI here
  document.getElementById('output').innerHTML = 
    `<pre>${JSON.stringify(data, null, 2)}</pre>`;
};

eventSource.onerror = (error) => {
  console.error('EventSource failed:', error);
  eventSource.close();
};

// Optional: Listen for specific event types
eventSource.addEventListener('custom-event', (event) => {
  console.log('Custom event:', event.data);
});
Enter fullscreen mode Exit fullscreen mode

Implementing Named Events

SSE supports named events for different data types:

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.flushHeaders();

  // Send notifications with named event
  const notificationInterval = setInterval(() => {
    res.write(`event: notification\n`);
    res.write(`data: ${JSON.stringify({ 
      id: Date.now(),
      message: 'New notification!'
    })}\n\n`);
  }, 5000);

  // Send heartbeat with different named event
  const heartbeatInterval = setInterval(() => {
    res.write(`event: heartbeat\n`);
    res.write(`data: ${JSON.stringify({ status: 'alive' })}\n\n`);
  }, 30000);

  req.on('close', () => {
    clearInterval(notificationInterval);
    clearInterval(heartbeatInterval);
    res.end();
  });
});
Enter fullscreen mode Exit fullscreen mode

Adding Reconnection and Retry Logic

The client can handle reconnection automatically, but you can customize it:

const eventSource = new EventSource('http://localhost:3000/events', {
  withCredentials: true
});

// Customize retry interval (in milliseconds)
let retryInterval = 5000;

eventSource.onerror = (error) => {
  if (eventSource.readyState === EventSource.CLOSED) {
    console.log('Connection closed, retrying...');
    // Implement custom retry logic if needed
    setTimeout(() => {
      console.log('Reconnecting...');
    }, retryInterval);
  }
};
Enter fullscreen mode Exit fullscreen mode

Building a Production-Ready SSE Service

For production, consider these improvements:

const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());

// Track all connected clients
const clients = new Set();

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
  res.flushHeaders();

  const clientId = Date.now();
  const clientData = { id: clientId, res };
  clients.add(clientData);

  console.log(`Client ${clientId} connected. Total: ${clients.size}`);

  res.write(`data: ${JSON.stringify({ 
    type: 'connected', 
    clientId 
  })}\n\n`);

  req.on('close', () => {
    clients.delete(clientData);
    console.log(`Client ${clientId} disconnected. Total: ${clients.size}`);
  });
});

// Broadcast to all connected clients
app.post('/broadcast', express.json(), (req, res) => {
  const { message } = req.body;

  clients.forEach((client) => {
    client.res.write(`data: ${JSON.stringify({ 
      type: 'broadcast',
      message 
    })}\n\n`);
  });

  res.json({ success: true, clients: clients.size });
});

app.listen(3000, () => console.log('Production SSE server running'));
Enter fullscreen mode Exit fullscreen mode

Using SSE with Express-Sse for Simplicity

For cleaner code, use the sse-express package:

npm install sse-express
Enter fullscreen mode Exit fullscreen mode
const SSE = require('sse-express');
const express = require('express');

const app = express();

app.get('/events', (req, res) => {
  const sse = new SSE(res);

  const interval = setInterval(() => {
    sse.send({ time: new Date().toISOString() });
  }, 1000);

  sse.on('close', () => {
    clearInterval(interval);
  });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

  1. Use nginx buffering: Disable for SSE
   proxy_buffering off;
   proxy_cache off;
Enter fullscreen mode Exit fullscreen mode
  1. Set appropriate timeouts: SSE connections can be long-lived. Configure your load balancer accordingly.

  2. Monitor connections: Track active connections and set limits to prevent resource exhaustion.

  3. Use HTTP/2: Multiple SSE streams can multiplex over a single HTTP/2 connection.

When to Use SSE vs WebSockets

Feature SSE WebSockets
Direction Server → Client Bidirectional
Protocol HTTP WS/WSS
Auto-reconnect Built-in Manual
Binary data Base64 required Native support
Browser support Modern browsers All modern browsers
Firewalls Usually works May be blocked

Conclusion

Server-Sent Events provide a simple, HTTP-native way to implement real-time features in your APIs. They're perfect for one-way data streams like notifications, live updates, and monitoring dashboards. With the examples in this guide, you can start building SSE-powered features today.

For more advanced use cases, consider combining SSE with a message queue (like Redis Pub/Sub) to scale across multiple server instances.


This guide was published on March 1, 2026 as part of the 1xAPI developer education series.

Top comments (0)