SSE - How to implement automatic updates from a server via HTTP (notifications, feed updates, etc)

How to get live updates from the server without having to constantly send request from the client. No WebSockets, only via HTTP!

Wed Sep 15 2021 | 6 minutes read
DEV

Illustration : Source

You could say that the notions of client and server are pretty much synonyms to almost the entire idea of the internet overall. Most of the apps we use or develop use some sort of requests, usually to a server. Our device sends a requests to a server, the server handles some business logic and sends us a nice response that our app can use to perform its shenanigans.

Most of the time spent at my job uses this communication method. Our front-end sends a request to an API, the API acts as a 'public' layer with a lot of magic behind it (authentication, database querying, data assembly and so on) and then we get back the goodies, usually in JSON format. Lately I've been looking at other types of client server communications. It's quite interesting to try and imagine how websites like Twitter update their feed in Realtime or how multiplayer browser games can keep everything in sync.

I've been reading about quite a few techniques such as:

  • WebSockets, currently reading about one of its implementations : Socket.io
  • Short Polling/Long Polling
  • Streaming over HTTP
  • Message Queues
  • HTML5 Event Source

I'll handle the other parts in subsequent blog posts but one of the easiest to comprehend and implement was the HTML5 Event Source which I had no idea existed before.

Server Side Events

By creating an EventSource instance, the browser opens a persistent connection to a HTTP Server, that gets its information in a 'text/event-stream format'. The opened connection is one-directional. The client doesn't need to ask the server for data regularly, the server can just send data while the connection is open. Everything functions on the HTTP Application layer, so no need to go down the OSI Layer and perform a TCP connection like in WebSockets.

With the connection being open, the server can send data in form of events or messages which are the handled by the front-end code to perform various updates within the UI, the store or its web storage. It's really useful for websites which handle data that updated periodically like feeds, notifications and so on.

I've set up some quick JS code in a React app to showcase how to use the EventSource:

useEffect(() => {
        const sse = new EventSource('http://localhost:8900/events');

        function getData(data) {
            setNotification(data);
            //Close the notification after 2 seconds
            setTimeout(() => setNotification(null), 2000);
        }

        sse.onmessage = e => {
                getData(JSON.parse(e.data));
        }
        sse.onerror = () => {
            console.log("Error occurred, closing the connection...")
            sse.close();
        }
        return () => {
            sse.close();
        };
    },[]);

`

The EventSource has three event handlers : onerror, onmessage and onopen which are called when those specific events occur. For example, in my code, every time a message is sent from the server, I show a notification.

For the backend, I set up a simple Express server, that sends out messages regurarly and every fifth message is a warning message.

app.get('/events', (req,res) => {
   console.log("Requested at /events");
   res.set({
       'Cache-Control' : 'no-cache',
       'Content-Type' : 'text/event-stream',
       'Connection' : 'keep-alive'
   });
   res.flushHeaders();
   res.write('retry: 10000\n\n');

   setInterval(() => {
       res.write("id: " + Date.now() +"\n\n");

       if(count%5===0){
           const facts = {
               type : 'warning',
               message : 'Server has issued a warning'
           }
           const data = `data: ${JSON.stringify(facts)}\n\n`;
           count++;
           res.write(data);
       }
       else{
           const facts = {
               type : 'info',
               message : 'User Azuro has posted!'
           }
           const data = `data: ${JSON.stringify(facts)}\n\n`;
           count++;
           res.write(data);
       }
   }, 3000);
});

The crucial lines of code within this piece of code are these:

res.set({
       'Cache-Control' : 'no-cache',
       'Content-Type' : 'text/event-stream',
       'Connection' : 'keep-alive'
   });
   res.flushHeaders();
   res.write('retry: 10000\n\n');

Here, we set the headers for the response : we specify that we don't require cache, the content type should be of type event-stream and we want to keep the connection alive. After that sending the data is just a matter of using res.write('data : [your-data-here]`). Note: Following this format is important.

That's it, hope you learned something new! Keep tuned for more quick posts about this topic!

Cheers,

Robert

Robert N

Hi, I'm Robert and I currently work as a Front-End Technical Lead. What can you find here? Some of my various experiments, science, tech, coding, the occasional digital art. Learning and sharing is cool, mkay?


Comments

Write a comment

  • Please keep things civil and respectful.
  • I will remove any comments which represent spam.
You have to be logged in to post a comment.

master of SOME

Robert 2025. Bunnies are awesome, don't you think so?