5
  • I am using the new version of Nuxt which does not come with a server folder
  • In the old version, you had a server folder and an index.js which contained the app
  • I want to add the websocket WS library with the new version of NuxtJS

The library requires an instance of server which you create by calling http.createServer(app) meaning an instance of the running express app. You would use this server instance now to listen to 3000 in the index.js file. Also create a sessionHandler which you obtain by calling session({}) with the express-session library

    const WebSocket = require('ws')
    function websocket({ server, sessionHandler, logger }) {
      // https://github.com/websockets/ws/blob/master/examples/express-session-parse/index.js, if you pass a server instance here 'upgrade' handler will crash
      const wss = new WebSocket.Server({ noServer: true })
      function noop() {}
      function heartbeat() {
        this.isAlive = true
      }
      wss.on('connection', (ws, request, client) => {
        ws.isAlive = true
        ws.on('pong', heartbeat)
        ws.on('message', (msg) => {
          logger.info(`Received message ${msg} from user ${client}`)
          ws.send(true)
        })
      })
      server.on('upgrade', (request, socket, head) => {
        sessionHandler(request, {}, () => {
          logger.info(`${JSON.stringify(request.session)} WEBSOCKET SESSION PARSED`)
          wss.handleUpgrade(request, socket, head, (ws) => {
            wss.emit('connection', ws, request)
          })
        })
      })
      // TODO use a setTimeout here  instead of a setInterval
      setInterval(function ping() {
        // wss.clients => Set
        wss.clients.forEach(function each(ws) {
          if (ws.isAlive === false) return ws.terminate()
          ws.isAlive = false
          ws.ping(noop)
        })
      }, 30000)
      return wss
    }
    module.exports = websocket
  • Does anyone know how I can make this work on the new Nuxt setup without the server folder
Just a coder
  • 15,480
  • 16
  • 85
  • 138
PirateApp
  • 5,433
  • 4
  • 57
  • 90
  • @tony19 just tested it, while it is true that npx-create-app gives you the option to choose express server, i dont see any server folder inside – PirateApp Aug 05 '20 at 04:04
  • @tony19 https://imgur.com/a/h851j59 you can see here that despite doing everything as suggested by you, the server folder is not there – PirateApp Aug 05 '20 at 08:39
  • 1
    I thought I was using the latest version of `create-nuxt-app`, but I was actually using an older cached version that still had the server templates. – tony19 Aug 05 '20 at 16:10

1 Answers1

13

You can create a custom module and use nuxt hooks to get a server instance on listen event.

Create modules/ws.js:

const WebSocket = require('ws')
const wss = new WebSocket.Server({ noServer: true })

wss.on('connection', ws => {
  ws.on('message', message => {
    console.log('received: %s', message);
  })
  ws.send('Hello')
})

export default function () {
  this.nuxt.hook('listen', server => {
    server.on('upgrade', (request, socket, head) => {
      wss.handleUpgrade(request, socket, head, ws => {
        wss.emit('connection', ws);
      })
    })
  })
}

And register the module in nuxt.config.js:

export default {
  modules: [
    '~/modules/ws'
  ]
}

In your case you could create a module directory instead of a single file to store multiple related files.

Create modules/ws/index.js

const websocket = require('./websocket') // this is your file

export default function () {
  this.nuxt.hook('listen', server => {
    websocket({
      server,
      sessionHandler (request, _, cb) { // example
        cb()
      },
      logger: console // example
    })
  })
}

Then copy your file to modules/ws/websocket.js. You can use module.exports which is CommonJS format or change it into ES Module format, Nuxt(Webpack) can handle that.

In your code I notice that ws.send(true) cause an error TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type boolean (true) which basically mean you cannot send boolean.

User 28
  • 4,863
  • 1
  • 20
  • 35
  • how would you import the existing function into this file and then export the server.on upgrade part – PirateApp Aug 05 '20 at 05:07
  • @PirateApp I'm not sure I understand you correctly, I update my answer. – User 28 Aug 06 '20 at 03:20
  • ok lets say inside a REST api endpoint i want to send websocket data to all clients, wss.clients.forEach(function each(ws) {ws.send(JSON.stringify({action: 'news/UPDATE_NEWS_WS',data,}))}) how do I access this wss istance inside a controller? – PirateApp Mar 19 '21 at 04:49
  • 1
    @PirateApp I think the easiest way is export the `wss` and then import it in whatever file you use. [Example](https://pastebin.com/YuSpkk0t). Or you may use [addServerMiddleware](https://nuxtjs.org/docs/2.x/internals-glossary/internals-module-container#addservermiddleware-middleware) (at module) to add custom middleware then pass `wss` via request object. I didn't test these but hope its works. – User 28 Mar 19 '21 at 05:55
  • thank you very much @User 28 I was able to get everything working with sessions! – PirateApp Mar 19 '21 at 08:53
  • Firstly, top notch answer, thanks for this. And then, can this be modified to listen to normal http connections, and if so, how? Or will I have to use server middleware for that? – SeriousLee Sep 08 '22 at 13:47
  • 1
    @SeriousLee I'm not sure that would be possible but you might try this `server.on('request', (req, res) => {...})` https://nodejs.org/dist/latest-v16.x/docs/api/http.html#:~:text=server.on(%27request%27%2C%20(request%2C%20res)%20%3D%3E%20%7B. However, I would use a serverMiddleware if possible. – User 28 Sep 08 '22 at 16:59
  • @User28 thanks for getting back to me, you're right, it doesn't look like it can be extended to handling api requests. Could you perchance advise on how I might connect the two if I go the server middleware route? There's very little on google about any of this. Basically, I'm running a regular Express api server in my server middleware that has a single route on it. When the route gets hit, I want to notify one of the clients subscribed to the wss in the hook. But I can't seem to figure out how to get the hook or wss or any of it accessed in the middleware. – SeriousLee Sep 09 '22 at 04:16
  • @User28 I made a separate post about it, if you'd care to take a look: https://stackoverflow.com/q/73657483/6048715 – SeriousLee Sep 09 '22 at 04:58
  • the hook listen does not work in production – Adri HM Dec 08 '22 at 17:57