Server-Sent Events in Node.js

Server-sent events are a simpler alternative to WebSockets.  Because server-sent events operate over HTTP, they can be used with standard web servers.  However, because server-sent events keep the HTTP connections open, they aren’t necessarily an ideal match for servers like Apache, which delegate operating system threads to handle individual requests.  Unlike Apache, Node.js, is single-threaded, and handles each request in an event loop.  This makes Node.js a better candidate for handling server-sent events.

This post provides step-by-step instructions for implementing server-sent events in the Node.js framework.  This post is based on version 0.8.6 of Node.js, running on Windows 7.  I expect that the steps outlined below should be somewhat similar for other versions and operating systems.  This post is not meant as a tutorial on server-sent events, however a tutorial on the server side aspects is available.  A similar tutorial covering the client side will be available in the near future.

What the Code Does

The demo in this post continuously streams the current date and time to the client, effectively creating a clock.  When the browser navigates to the server, an EventStream is opened.  The user can disconnect from, and reconnect to, the stream at any time using a button on the page.  When the stream is active, the server sends a JavaScript Date object to the client once every second.  If the connection is disrupted for any reason, the client will attempt to automatically reconnect every ten seconds.  A custom “connecttime” event is also demonstrated each time the client establishes a connection to the server.

Installing Node.js

Node.js can be downloaded from the project’s homepage.  Download a prebuilt version of Node.js by selecting the Windows Installer option.  Next, run the installer and locate the resulting Node.js installation directory.  On my machine, the installer configured Node.js in the “C:\Program Files (x86)\nodejs” directory.  If necessary, change the permissions of the “nodejs” directory so that you can create/modify files.

Creating the Server

Node.js executes standalone JavaScript files.  In the “nodejs” folder, create a file named “sse_server.js” ― this will be the server.  Copy the following code into “sse_server.js”.

var http = require("http");
var fs = require("fs");

http.createServer(function (req, res) {
  var index = "./sse.htm";
  var fileName;
  var interval;

  if (req.url === "/")
    fileName = index;
  else
    fileName = "." + req.url;

  if (fileName === "./stream") {
    res.writeHead(200, {"Content-Type":"text/event-stream", "Cache-Control":"no-cache", "Connection":"keep-alive"});
    res.write("retry: 10000\n");
    res.write("event: connecttime\n");
    res.write("data: " + (new Date()) + "\n\n");
    res.write("data: " + (new Date()) + "\n\n");

    interval = setInterval(function() {
      res.write("data: " + (new Date()) + "\n\n");
    }, 1000);
    req.connection.addListener("close", function () {
      clearInterval(interval);
    }, false);
  } else if (fileName === index) {
    fs.exists(fileName, function(exists) {
      if (exists) {
        fs.readFile(fileName, function(error, content) {
          if (error) {
            res.writeHead(500);
            res.end();
          } else {
            res.writeHead(200, {"Content-Type":"text/html"});
            res.end(content, "utf-8");
          }
        });
      } else {
        res.writeHead(404);
        res.end();
      }
    });
  } else {
    res.writeHead(404);
    res.end();
  }

}).listen(80, "127.0.0.1");
console.log("Server running at http://127.0.0.1:80/");

Client Page

The next step is to create a client page which can subscribe to the event stream.  The server code expects the client page to be stored in “sse.htm”.  Create “sse.htm” in the “nodejs” directory and copy the following code into it.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Server-Sent Events Demo</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      var button = document.getElementById("connect");
      var status = document.getElementById("status");
      var output = document.getElementById("output");
      var connectTime = document.getElementById("connecttime");
      var source;

      function connect() {
        source = new EventSource("stream");
        source.addEventListener("message", function(event) {
          output.textContent = event.data;
        }, false);

        source.addEventListener("connecttime", function(event) {
          connectTime.textContent = "Connection was last established at: " + event.data;
        }, false);

        source.addEventListener("open", function(event) {
          button.value = "Disconnect";
          button.onclick = function(event) {
            source.close();
            button.value = "Connect";
            button.onclick = connect;
            status.textContent = "Connection closed!";
          };
          status.textContent = "Connection open!";
        }, false);

        source.addEventListener("error", function(event) {
          if (event.target.readyState === EventSource.CLOSED) {
            source.close();
            status.textContent = "Connection closed!";
          } else if (event.target.readyState === EventSource.CONNECTING) {
            status.textContent = "Connection closed. Attempting to reconnect!";
          } else {
            status.textContent = "Connection closed. Unknown error!";
          }
        }, false);
      }

      if (!!window.EventSource) {
        connect();
      } else {
        button.style.display = "none";
        status.textContent = "Sorry, your browser doesn't support server-sent events";
      }
    }, false);
  </script>
</head>
<body>
  <input type="button" id="connect" value="Connect" /><br />
  <span id="status">Connection closed!</span><br />
  <span id="connecttime"></span><br />
  <span id="output"></span>
</body>
</html>

Running the Code

Start the server by opening a command prompt in the “nodejs” directory and then typing the following command:

node sse_server.js

If everything works properly, the server will display a message that it is running at http://127.0.0.1:80.  Finally, the server can be accessed by opening a browser window and navigating to “http://localhost” or “http://localhost/sse.htm“.  Note that your browser must support server-sent events.


2 thoughts on “Server-Sent Events in Node.js

  1. Thank you so much for less. I’ve never been that much of a JavaScript fan, preferring server side solutions, but I have need now where to avoid a browser plugin (hate them) I really do need to implement SSE via JS on the client and ?? on the server. Looks like apache, my standard goto web server, really is not well suited so I probably will be using node.js and thus using JS on the server itself too ;)

    There’s not a whole lot out there on SSE right now, so thank you for this!