Home > Blockchain >  socket.io | Should I wrap my route handlers inside io.on('connection')?
socket.io | Should I wrap my route handlers inside io.on('connection')?

Time:11-10

In the code below, I'm assuming there is a chance that my route handler will fire and try to emit to the socket before the io connection has been established:

server.js:

import { Server } from 'socket.io'
....
....
const app = express()
const io = new Server(....)

app.io = io

app.post('/something', (req, res) => {
  req.app.io.emit('something', doSomethingWith(req.body))
  res.status(200)
})


io.on('connection', function(socket) {
  console.log('socket connected')

  socket.on('disconnect', (reason) => {
    console.log('disconnected due to = ', reason)
  })

})

client.js:

socket = io(`http://localhost:${port}`, { transports: ['websocket'] })

socket.on('something', (data) => {
  doSomethingMoreWith(data)
})

fetch('/something', ....)

In that case, is it safer to instead do:

io.on('connection', function(socket) {
  app.post('/something', ....)
  app.get('/something', ....)
  .....
  ...

  socket.on('disconnect', (reason) => {
    console.log('disconnected due to = ', reason)
  })

})

Or is this is not recommended and there is a better option ?

CodePudding user response:

Putting app.post() and app.get() inside of io.on('connection', ...) is never the proper design or implementation. This is because io.on('connection', ...) is triggered multiple times and there's no point in adding the same express route handler over and over again as that will just waste memory and do nothing useful. The very first client to connect on socket.io would cause the routes to be registered and they'd be there from then on for all other clients (whether they connected via socket.io or not).

It is unclear why you are trying to do this. You don't install routes for one particular circumstance. Routes are installed once for all clients in all states. So, if you're trying to conditionally install routes, that type of design does not work. If you further explain what you're trying to accomplish, then perhaps we could make some different suggestions for a design.

In the code below, I'm assuming there is a chance that my route handler will fire and try to emit to the socket before the io connection has been established:

app.post('/something', (req, res) => {
  req.app.io.emit('something', doSomethingWith(req.body))
  res.status(200)
});

How exactly this code works depends upon what is doing the POST. If it's Javascript in an existing page, then that page will already by up and initialized and you control (with your Javascript client code in the page) whether you wait to issue the POST to /something until after the socket.io connection is established.

If this POST is a regular browser-based form submission (no Javascript involved), then you have other problems because a form submission from a browser reloads the current browser page with the response from the POST and, in the process of reloading the page, kills any existing socket.io connection that page had (since it loads a new page). Since you're not sending any content back from the POST, this would result in an empty page being displayed in the browser and no socket.io connection.

In looking at your client code, it appears that perhaps the POST is coming from a fetch() in the client code (and thus entirely Javascript-based). If that's the case, I would suggest restructuring your client code so that it waits until the socket.io connection has finished connecting before doing the fetch(). That way, you know you will be able to receive the io.emit() that the server does.

socket = io(`http://localhost:${port}`, { transports: ['websocket'] })

socket.on('something', (data) => {
  doSomethingMoreWith(data)
});

socket.on('connect', () => {
   // only issue fetch after socket.io connection is operational
  fetch('/something', ....)
});
  • Related