DEV Community

Cover image for EventSource: Real-Time Made Easy
Nícolas Gabriel
Nícolas Gabriel

Posted on

EventSource: Real-Time Made Easy

Often when you have a problem where you need real-time communication the first thing that comes to mind is... of course, WebSocket, right?

And from a certain perspective, you're not wrong! But at the same time, maybe other options could better fit your use case. Today I want to talk about one of them that is not frequently mentioned: EventSource.

What is it?

EventSource is widely available for all browsers, and it is dead simple.

It’s essentially an HTTP request that stays open, allowing the server to continuously push “messages” to the client as long as the connection remains active.

Those messages, just like WebSocket messages, are strings. So you could either send a "Hello world", or a { "hello": "World" }. Making it a viable option thanks to its simplicity and ease of implementation.

Why not WebSocket?

When considering that, the most important criteria is actually your use case! Because EventSource has one really important limitation: It only sends data from server to client, not client to server. That is, it is a unidirectional communication, not bidirectional like WebSocket.

Given that it is a great fit for use cases where the client only needs to receive updates but not send them. Think of things like live notifications, real-time dashboards, stock prices, sports scores, log streams, or live comment feeds. Anywhere the server pushes data and the client listens, SSE can shine.

But considering that your use case matches that limitation, then EventSource is a good option for you! It avoids all the WebSocket configuration that you need to do in order to support it both for server and client.

But okay, theory is great and nice, but "talk is cheap, show me the code" right? So...

How do I implement it?

Since we have two sides, Backend and Frontend, let's start by the one who initiates the connection, which is the client.

Client (Browser)

The underlying logic differs a bit from WebSocket since it runs over plain HTTP, but the high level logic is the same, it:

  1. Client sends a request to connect to the server.
  2. The server sends back a response with "text/event-stream" content type
  3. client knows it accepted and passively listens for new messages.

In code, this listen is done like this:

const source = new EventSource("https://yourapi.com/notifications")

source.onopen = () => console.log("EventSource started!")

source.onmessage = (event) => {
  // If it is plain text:
  console.log(event.data)
  // If it is a JSON:
  console.log(JSON.parse(event.data))
}

source.onerror = () => console.error("EventSource failed!")
Enter fullscreen mode Exit fullscreen mode

And that's it. As you can see, you can do everything in less than 10 lines of code!

But the server is a bit more varied, as it may change depending on which language and framework you choose, but let's go!

Server

Presuming you're into the Node.JS ecosystem I'll use express in the example.

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

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

  const send = () => {
    const data = {
      message: 'Hello from server',
      timestamp: new Date().toISOString()
    };
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  };

  // So every 2s, it will send a new message!
  const interval = setInterval(send, 2000);

  req.on('close', () => clearInterval(interval));
});
Enter fullscreen mode Exit fullscreen mode

And with that you're going to keep sending notifications when the client connects to the route.
Just some observations:

  • The "text/event-stream" header is mandatory, it is needed for the browser to understand that it needs to keep listening instead of simply closing upon first response.
  • The "no-cache" header isn't mandatory, but it's a good practice since we want to send data dynamically instead of just once from cache.

Final considerations

As I mentioned earlier, the biggest benefit is avoiding WebSocket configs, but if you're already using it for some other reason, then it's up to you, since both seem good options!

Regarding disconnection, the Browser automatically tries to reconnect if the connection was abruptly interrupted, but not if intentionally closed by one the sides.

Given all that, now you know one more alternative to speak of when people ask you about server-to-client communication! 😉

If you still have any questions, feel free to leave a comment. I hope this article helped you, see ya!

Top comments (4)

Collapse
 
nevodavid profile image
Nevo David

Nice writeup - been ignoring EventSource for years and now I’m rethinking some choices lol.

Collapse
 
nickgabe profile image
Nícolas Gabriel • Edited

hahaha, i can say the same about my old self!
and tysm! :)

Collapse
 
kauefraga profile image
Kauê Fraga Rodrigues

Good article! Earlier this year I created a project using websockets to render the updated version of a markdown file as soon as it was changed. Someone suggested that I use SSE and since the use case really didn't require the client to send data, I moved to Server-Sent Events and the implementation became much simpler.

The project I mentioned is Pavus.

Collapse
 
nickgabe profile image
Nícolas Gabriel

Amazing! SSE really simplifies things, it's sad that people often don't event know it exists, nice to hear it worked well for you :)

And interesting project btw