Search code examples
expresswebsocketexpress-ws

How to set up a WebSocket server with Express.js (properly)?


Please let me know if this is a duplicate.

Description

I'll try to be concise. I have a barebones Express API with a WebSocket endpoint that I set up with express-ws following the docs, but it doesn't work. I can't tell where the problem lies—client-side or server-side.

What I can tell is that FF (v112.0b6 dev ed) does not/cannot connect to my WebSocket endpoint.

Server

Dir structure

src/
  api/
    index.ts
  app.ts
  index.ts

src/index.ts

import app from './app'

const port = process.env.PORT || 80

app.listen(port, () => {
    console.log(`Listening on http://localhost:${port}`)
})

src/app.ts

import express from 'express'
import api from './api'

const app = express()
app.use('/api/v1', api)

export default app

src/api/index.ts

import express from 'express' // v4.18.1
import expressWs from 'express-ws' // v5.0.2

expressWs(express())

const router = express.Router()

router.ws('/', (ws, req) => {
    ws.on('message', msg => {
        console.log(msg)
    })
})

export default router

Server console

Listening on http://localhost:41240

BTW, other HTTP routers work fine on this app.

Client

Browser console

const socket = new WebSocket('ws://localhost:41240/api/v1')

socket.addEventListener('open', () => {
    socket.send('iloveyou')
})

The browser console is open on http://localhost:41240 (same host).

Result

Browser console

Firefox can’t establish a connection to the server at ws://localhost:41240/api/v1.

Sensibly, the client-side code does not execute further than the declaration of the socket, since that's where the connection fails.

Thank you for any help and do let me know if you need additional details.


Solution

  • This line of /src/api/index.ts:

    expressWs(express())
    

    is not correct. That is creating a new app object that is not connected to your running http server. That app needs to be the SAME app object that the rest of your app is using, not a new one. Probably whoever imports this module needs to pass it the app object in some initialization function and you can then initialize expressWs using the shared app object.

    In this expressWs doc example, you can see that there's only one app object that both express and expressWs are using, plus it makes sense that expressWs needs to be able to hook into your existing web server.