Search code examples
javascriptnode.jswebsocketserver-sent-events

Use server side events to dynamically send message to browser


I was hoping to use SSE to dynamically send messages to the browser. Ideally I would like a minimal application where the browser does nothing until a function or method (which takes the message as it's argument) is called and the browser receives this message an logs it only once. I have tried to illustrate this with the below:

const http = require("http");
const server = http.createServer(<not sure what code goes here>);
server.listen(8000);

// this can be called at any time after creating the server so the browser 
// can receive the message and log it to the console.
sendMessageToBrowser(`data: this is a dynamic message\n\n`) 

However, the below basic SSE application below simply logs "hello world" to the browser console every 3 seconds (the default). I don't understand how this is any different to serving data via a regular route and using something like:

setInterval(fetch("/events").then(res => res.text()).then(data => console.log(data)));

Is my request possible with SSE or have I misunderstood how it works? I know my request is possible with websockets/socket.io but was hoping to use SSE as I don't want to use a library and SSE simpler to understand and implement.

Minimal example which logs hello world every 3 seconds:

const http = require("http");

const server = http.createServer((req, res) => {
  // Server-sent events endpoint
  if (req.url === "/events") {
    res.writeHead(200, {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      Connection: "keep-alive",
    });
    res.end(`data: hello world\n\n`);
    return;
  }

  // Client side logs message data to console
  res.writeHead(200, { "Content-Type": "text/html" });
  res.end(`
      <script>
        var eventSource = new EventSource('/events');
        eventSource.onmessage = function(event) {
          console.log(event.data);
        };
      </script>
  `);
});

server.listen(8000);

Solution

  • This is a minimal version of @Mattia's answer.

    const http = require("http");
    var resEvents;
    
    const server = http.createServer((req, res) => {
      if (req.url === "/events") {
        resEvents = res;
        res.writeHead(200, {
          "Content-Type": "text/event-stream",
          "Cache-Control": "no-cache",
          Connection: "keep-alive",
        });
        return;
      }
    
      res.writeHead(200, { "Content-Type": "text/html" });
      res.end(`
          <script>
            var eventSource = new EventSource('/events');
            eventSource.onmessage = function(event) {
              console.log(event.data);
            };
          </script>
      `);
    });
    
    server.listen(8000);
    
    // this can only be run after the browser has connected - either run code
    // sequentially in a REPL or put this in a setTimeout() to give you time 
    // to go to localhost:8000
    resEvents.write(`data: hello\n\n`);