How do I start websocket server (socketIO or otherwise) in Nuxt 3? Does not work the same as in Nuxt 2

Viewed 565

I am trying to convert my code from Nuxt 2 to Nuxt 3, and I have run into an issue with creating a websocket server in Nuxt 3.

It works perfectly fine in Nuxt 2 using this code:

// Nuxt 2: modules/socket.js
import http from 'http'
import socketIO from 'socket.io'

export default function () {
    this.nuxt.hook('render:before', () => {
        const server = http.createServer(this.nuxt.renderer.app)
        const io = socketIO(server)

        this.nuxt.server.listen = (port, host) => new Promise(resolve => server.listen(port || 3000, host || 'localhost', resolve))
        this.nuxt.hook('close', () => new Promise(server.close))

        io.on('connection', (socket) => {
            console.log("CONNECTED")
        })
    })
}
// Nuxt 2: plugins/socket.client.js
import io from 'socket.io-client'
const socket = io('http://localhost:3000')

export default ({}, inject) => {
    inject('socket', socket)
}
<!-- Nuxt 2: pages/index.vue -->
<template>
<div>
    <p>Check socket status in Vue devtools...</p>
</div>
</template>

<script>
export default {
    computed: {
        socket() {
            return this.$socket ? this.$socket : {};
        }
    }
}
</script>

However, in Nuxt 3 I cannot access this.nuxt.renderer.app in the modules/socket.js file (for http.createServer(...)), and I cannot figure out how to access the correct renderer.app elsewhere in a Nuxt3 module. My Nuxt 3 code looks like this:

// Nuxt 3: modules/socket.js
import http from 'http'
import socketIO from 'socket.io'

export default (_, nuxt) => {
    // Note that I use the 'ready' hook here - render:before is apparently not included in Nuxt3.
    nuxt.hook('ready', renderer => {
        // nuxt.renderer is undefined, so I've tried with renderer.app instead, with no luck.
        const server = http.createServer(renderer.app)
        const io = socketIO(server)

        nuxt.server.listen = (port, host) => new Promise(resolve => server.listen(port || 3000, host || 'localhost', resolve))
        nuxt.hook('close', () => new Promise(server.close))
            
        io.on('connection', () => {
            console.log("CONNECTION")
        })
    })
}
// Nuxt 3: plugins/socket.client.js
import io from 'socket.io-client'

export default defineNuxtPlugin(() => {
    const socket = io('http://localhost:3000')

    return {
        provide: {
            socket: socket
        }
    }
})
<!-- Nuxt 3: app.vue -->
<template>
  <div>
      <p>Check socket status in Vue devtools...</p>
  </div>
</template>

<script setup>
    const { $socket } = useNuxtApp()    
</script>

I would make a codesandbox link for you, but every time I try, it breaks before I even add any code. I think it does not correctly work with Nuxt3 yet. Has anyone successfully established a websocket server in a Nuxt 3 module yet? Or can anyone see what I am missing?

I am interested in any working solution, it does not necessarily have to be socket.io.

3 Answers

I figured it out!

Digging deep into the Nuxt 3 code, it turns out that they have a listen hook which provides the server parameter that I needed to set up the server. This information is really hard to find though.

I also managed to simplify the script a bit.

Here's the updated modules/socket.js:

import { Server } from 'socket.io'

export default (_, nuxt) => {
    nuxt.hook('listen', server => {
        const io = new Server(server)

        nuxt.hook('close', () => io.close())
        
        io.on('connection', () => {
            console.log("CONNECTION")
        })
    })
}

Everything else can remain the same

thx anbork I'm use ws it the same method create file "modules/wsserver.ts"

import { WebSocketServer } from "ws"

export default (_: any, nuxt: any) => {
  nuxt.hook("listen", (server: any) => {
    const wss = new WebSocketServer({ server })

    nuxt.hook("close", () => wss.close())

    wss.on("connection", function connection(ws) {
        ws.on("message", function message(data) {
            console.log("received: %s", data)
        })

        ws.send("something")
    })
})

}

then register at nuxt.config

 modules: ["./modules/wsserver"],

Based on @ahbork's response and a addition from the Nuxt 3 docs, I got this on Vue 3 + Nuxt 3 + Typescript:

import { Server } from 'socket.io'
import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    nuxt.hook('listen', (server) => {
      console.log('Socket listen', server.address(), server.eventNames())
      const io = new Server(server)

      nuxt.hook('close', () => io.close())

      io.on('connection', (socket) => {
        console.log('Connection', socket.id)
      })

      io.on('connect', (socket) => {
        socket.emit('message', `welcome ${socket.id}`)
        socket.broadcast.emit('message', `${socket.id} joined`)

        socket.on('message', function message(data: any) {
          console.log('message received: %s', data)
          socket.emit('message', { data })
        })

        socket.on('disconnecting', () => {
          console.log('disconnected', socket.id)
          socket.broadcast.emit('message', `${socket.id} left`)
        })
      })
    })
  },
})
Related