Websockets in an Express node.js app on Heroku

Last night I was having an issue with websockets and TLS in an Express.js node.js app. My websocket was working just fine when developing locally over plain HTTP but when I deployed the app to Heroku I received an error as that app runs over HTTPS but the websocket was still plain HTTP (using ws:// instead of wss://). Hmmm…. I started digging into websockets over TLS and how that would work without any luck. So I asked around but then it dawned on me and I answered my own question… Sometimes finding the answer is all about the question you ask 🙂

So the main realisation is that TLS connections are terminated by the Heroku router and forwarded to a web dyno hence there was no need to listen for TLS based websocket connections in my app. Also remembering how websocket connections are created is important. A websocket connection is a normal HTTP connection which is then upgraded to a websocket connection. So the real solution was to understand how a web dyno in node.js using Express could share it’s port with websockets using a HTTP server and that the same HTTP server would be used for both HTTP transport and websocket connections.

The solution was as follows:

const express = require('express')
const http = require('http')
const WebSocket = require('ws')

const port = process.env.PORT || 8080
const app = express()
const httpServer = http.createServer(app)
const wss = new WebSocket.Server({
    'server': httpServer
})
httpServer.listen(port)

So in essence:

  1. Create the Express.js app (but do not set it up to listen in a port)
  2. Create HTTP server in node.js passing in the Express.js app
  3. Create websocket server agin using the HTTP server as the server
  4. Make the HTTP server listen on the port provided through environment variable