DEV Community

Cover image for Server-Sent Events: A Simple Solution you didn't know you needed
Vignesh Murugan
Vignesh Murugan

Posted on

Server-Sent Events: A Simple Solution you didn't know you needed

Server-Sent Events

is a client-server communication technique with a one-way communication flow. Yes, as it implies the server will sent the data to the client while the client initially establishes a connection using Event Source and sits idle.

I'm familiar with communication techniques like Short polling, Long polling, and Web Sockets and I implemented them at certain stages in my development journey. 

There came a situation where I have to find and use the right technique to solve the problem at hand, and this article is all about why and how I did it.

When confused with what to use for a real-time app

Before moving here's a quick overview of each of the technique:

  • Short Polling:
    The client repeatedly requests data from the server at fixed intervals. Simple but inefficient as it creates unnecessary network traffic when there's no new data.

  • Long Polling:
    The client makes a request, and the server holds the connection open until it has new data to send. Less wasteful than short polling but can still be resource-intensive on the server.

  • Web Sockets:
    Creates a persistent, bi-directional connection between client and server. Great for real-time applications where both sides need to communicate freely, but can be overkill for simpler use cases.

  • Server Sent Events:
    Establishes a one-way connection from server to client. The server can push data whenever it's ready while maintaining a single persistent connection. Lightweight and built into standard HTTP.

Here's why I had to use Server-Sent Events in my case:

Client will make an API call to the Server consisting of multiple user's data from the dashboard, which the server will process one by one and send the results back one by one.

Initially I thought of using the Web Sockets for the above problem. But upon considering the nature of the problem, design architecture and the use-case it makes a clear sense to use Server-Sent Events.

Here's why:

  1. Simpler Implementation:
    SSE requires minimal setup compared to Web Sockets. The client-side code is surprisingly straightforward.

  2. One-way Communication Flow:
    In my case, data only needed to flow from server to client. The client requests the process to start, and then just listens for updates. No need for the overhead of bi-directional communication.

  3. Standard HTTP:
    No special protocols or ports needed, making it easier to work with existing infrastructure and firewalls.

Implementation

Here's a gist of how I implemented it:

Server-side (Node.js with Express):

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

  // Get users from the request
  const users = req.query.users.split(',');

  // Process each user and send results as they complete
  users.forEach((user, index) => {
    // Simulate processing time
    setTimeout(() => {
      const data = JSON.stringify({
        user,
        result: `Processed data for ${user}`,
        progress: ((index + 1) / users.length) * 100
      });

      // Send the event
      res.write(`data: ${data}\n\n`);

      // If last user, end the response
      if (index === users.length - 1) {
        res.write(`data: ${JSON.stringify({ done: true })}\n\n`);
      }
    }, index * 1000); // Process each user with a delay
  });
});

Enter fullscreen mode Exit fullscreen mode

Client-side:

function processUserData(users) {
  // Create the EventSource
  const eventSource = new EventSource(`/api/process-data?users=${users.join(',')}`);

  // Handle incoming messages
  eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);

    if (data.done) {
      console.log('Processing complete!');
      eventSource.close();
      return;
    }

    console.log(`User: ${data.user}, Result: ${data.result}`);
    updateProgressBar(data.progress);
  };

  // Error handling
  eventSource.onerror = (error) => {
    console.error('SSE error:', error);
    eventSource.close();
  };
}

function updateProgressBar(progress) {
  const progressBar = document.querySelector('.progress-bar');
  progressBar.style.width = `${progress}%`;
  progressBar.textContent = `${Math.round(progress)}%`;
}

Enter fullscreen mode Exit fullscreen mode

Advantages I Discovered

  1. Bandwidth Efficiency:
    Unlike Web Sockets which maintain a constant connection, SSE only sends data when there's something to send.

  2. Easy Debugging:
    Since it's just HTTP, I could test and debug using standard tools like browser dev tools or Postman.

  3. Scalability:
    For my use case of processing user data sequentially, SSE scaled well without the connection management complexity of Web Sockets.
    Progressive Updates: Users see results as they become available, rather than waiting for all processing to complete.

Limitations to Keep in Mind:
It's not all sunshine and rainbows though. SSE does have some limitations:

  1. Connection Limits:
    Browsers typically limit the number of concurrent SSE connections (usually 6 per domain).

  2. One-way Communication Only:
    If you need the client to send data back frequently, SSE isn't the right choice.

  3. IE Support:
    If you need to support Internet Explorer, you'll need a polyfill.

Conclusion

Sometimes the simplest solution is the best one. While Web Sockets get all the glory in real-time applications, Server-Sent Events provided exactly what I needed with minimal overhead and complexity.

For use cases where you need server-to-client updates without the complexity of Web Sockets, give SSE a try. You might find it's the solution you didn't know you needed.

Have you used SSE in your projects? I'd love to hear about your experiences in the comments below!

Top comments (0)