Using WebSockets on Heroku with Node.js

Last Updated: 27 March 2014

node redis websockets

Table of Contents

This quickstart will get you going with a Node.js application that uses a WebSocket, deployed to Heroku.

The code for this demo application is available on GitHub and can be seen running at node-ws-test.herokuapp.com. Edits and enhancements are welcome. Just fork the repository, make your changes and send us a pull request.

Prerequisites

If you’re new to Heroku or Node.js development, you’ll need to set up a few things first:

Create WebSocket app

The sample application provides a simple example of using WebSockets with Node.js. You can clone the sample and follow along with the code as you read.

Option 1. Clone sample app

If you want to get going more quickly you can just clone the sample app:

$ git clone https://github.com/heroku-examples/node-ws-test.git
Cloning into 'node-ws-test'...
remote: Counting objects: 22, done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 22 (delta 6), reused 20 (delta 5)
Unpacking objects: 100% (22/22), done.
Checking connectivity... done

Option 2. Create new app

$ mkdir node-ws-test
$ cd node-ws-test

Functionality

The demo application is a simple timer app that will open a WebSocket to the backend and receive a timestamp from the server every second to be displayed on the page.

Server

The server is a Node.js app powered by express 3, node’s native http module, and the einaros/ws WebSocket implementation. Express is used to serve the static frontend.

When the server establishes a connection with a client, it declares a function that runs every 1000 ms, periodically sending a timestamp to the browser over the WebSocket. Here is the server.js definition:

var server = http.createServer(app);
server.listen(port);

var wss = new WebSocketServer({server: server});
console.log('websocket server created');
wss.on('connection', function(ws) {
  var id = setInterval(function() {
    ws.send(JSON.stringify(new Date()), function() {  });
  }, 1000);

  console.log('websocket connection open');

  ws.on('close', function() {
    console.log('websocket connection close');
    clearInterval(id);
  });
});

The close event handler ensures that interval timers aren’t still running after the client has disconnected from the server.

Client

The client makes use of the browser’s native WebSocket feature to establish a connection with the server, then dynamically appends each timestamp it receives from the server to the list. Here is the front-end end javascript in the index.html file that handles timestamps sent from the server:

var host = location.origin.replace(/^http/, 'ws')
var ws = new WebSocket(host);
ws.onmessage = function (event) {
  var li = document.createElement('li');
  li.innerHTML = JSON.parse(event.data);
  document.querySelector('#pings').appendChild(li);
};

Local execution

Confirm that your application runs locally before deploying to Heroku.

Dependencies

If you’re not working from the cloned sample app, you will need to create a package.json file to declare the app’s dependencies:

{
  "name": "node-ws-test",
  "version": "0.0.1",
  "repository": {
    "type": "git",
    "url": "git://github.com/einaros/ws.git"
  },
  "dependencies": {
    "ws": "0.4.x",
    "express": "3.x"
  },
  "engines": {
    "node": "0.10.x",
    "npm": "1.2.x"
  }
}

And install them with npm:

$ npm install
npm http GET https://registry.npmjs.org/ws
npm http GET https://registry.npmjs.org/express
npm http 304 https://registry.npmjs.org/ws
npm http 304 https://registry.npmjs.org/express
npm http GET https://registry.npmjs.org/ws/-/ws-0.4.31.tgz
... etc

The application’s Procfile contains the server process definition you need to get up and running:

web: node server.js

Fire up the app with foreman:

$ foreman start
17:00:04 web.1  | started with pid 3999
17:00:05 web.1  | http server listening on 5000
17:00:05 web.1  | websocket server created
...

You should now have a Node.js webserver running locally at localhost:5000.

Deploy

It’s time to deploy your app to Heroku. If you haven’t done so already put your application into a git repository:

$ git init
$ git add .
$ git commit -m "Ready to deploy"

Create the Heroku app to deploy to:

$ heroku create
Creating boiling-bastion-2872... done, stack is cedar
http://boiling-bastion-2872.herokuapp.com/ | git@heroku.com:boiling-bastion-2872.git
Git remote heroku added

While in beta, WebSocket functionality must be enabled via the Heroku Labs:

$ heroku labs:enable websockets
Enabling websockets for boiling-bastion-2872... done
WARNING: This feature is experimental and may change or be removed without notice.
For more information see: https://devcenter.heroku.com/articles/heroku-labs-websockets

Deploy your code with git push.

$ git push heroku master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (20/20), done.
Writing objects: 100% (22/22), 3.32 KiB | 0 bytes/s, done.
Total 22 (delta 6), reused 0 (delta 0)

-----> Node.js app detected
-----> Resolving engine versions
       Using Node.js version: 0.10.20
       Using npm version: 1.2.30
-----> Fetching Node.js binaries
-----> Vendoring node into slug
-----> Installing dependencies with npm
       npm http GET https://registry.npmjs.org/express
       npm http GET https://registry.npmjs.org/ws
       npm http 200 https://registry.npmjs.org/ws
       npm http GET https://registry.npmjs.org/ws/-/ws-0.4.31.tgz
       npm http 200 https://registry.npmjs.org/express
       ... etc
       Dependencies installed
-----> Building runtime environment
-----> Discovering process types
       Procfile declares types -> web

-----> Compiled slug size: 16.4MB
-----> Launching... done, v4
       http://boiling-bastion-2872.herokuapp.com deployed to Heroku

To git@heroku.com:boiling-bastion-2872.git
 * [new branch]      master -> master

Congratulations! Your web app should now be up and running on Heroku. Open your newly deployed app in the browser:

$ heroku open

Next steps

This demo app only scratches the surface of what is possible using WebSockets.

The example app described in this article is only suitable for demonstration purposes. Production apps, and apps running more than a single web dyno, require an architecture that accounts for shared state between the dynos as described here.

For a deeper dive into building Node.js apps using WebSockets, see Geosockets, a Node.js server and client that renders website visitors on a map in realtime using WebSockets and the browser’s Geolocation API.

Additional topics

For more information about using Node.js and WebSockets on Heroku, see the following articles: