1

I'm building my first MERN app [a message board] and trying to add a mail notification using socket.io.

So far I have in server.js:

const io = socketio(expressServer);
const connectedUsers = {};

io.on('connection', (socket) => {
  socket.emit('messageFromServer', { data: 'Welcome to the server' });
  socket.on('messageToServer', (dataFromClient) => {
    connectedUsers[dataFromClient.username] = socket;
  });
});

to emit from my controller file, do I need the io object or just the socket, and how can I share it/them with the controller file?

All help greatly appreciated!

harry young
  • 600
  • 1
  • 8
  • 24
  • What do you mean by from my controller? Are you trying to link http get/post requests to a web socket? – Matt Sep 21 '20 at 23:29
  • yes, for relevant notifications :) – harry young Sep 21 '20 at 23:31
  • specifically, I want to send a notification to the user when another user sends them a private message – harry young Sep 21 '20 at 23:32
  • I see, that could be done with a lookup table for `username => socket.id`. Create it somewhere in your `io` snippet above when you authenticate the user (or have a user id for the socket). and clean up the entry on a `socket` `close` event. – Matt Sep 21 '20 at 23:40
  • 1
    sorry that should be the `socket` `disconnect` event – Matt Sep 21 '20 at 23:42
  • by lookup table, do you mean an object {} ? That was/is the purpose of 'connectedUsers'. I tried exporting it from the main server file with module.exports and importing it to the controller file where I wanted to use it, but to no avail... it was always null because [I assume...] The import happened before the user connected. That's the issue I'm having. Thanks for replying. – harry young Sep 21 '20 at 23:48
  • 1
    That export/require issue is probably a circular dependency. You are including the controller from the server, and the server from the controller. – Matt Sep 22 '20 at 00:12

1 Answers1

1

Setup an external file for your user tracking connectedUsers.js to avoid circular dependencies.

const connectedUsers = {}

module.exports = { connectedUsers }

Server Setup:

const { connectedUsers } = require('./connectedUsers')

io.on('connection', (socket) => {
  socket.on('messageToServer', (dataFromClient) => {
    connectedUsers[dataFromClient.username] = socket;
  });
});

The next problem is getting the io instance to the router/controller. I normally attach the io instance to the app in the server setup for this.

app.io = io

Then in the router/controller you can access the io instance from the req object:

const { connectedUsers } = require('./connectedUsers')

router.post('/message', function(req,res){
  if ( connectedUsers[req.body.username] && connectedUsers[req.body.username].id) {
     const id = connectedUsers[req.body.username].id
     req.app.io.to(id).emit({ msg: 'blah' })
  }
})

The if check above is an example of the code that will pop up everywhere you use connectedUsers. The simple object often turns into a singleton class ConnectedUsers {} instance so common code can be attached rather that littered throughout controllers.

Matt
  • 68,711
  • 7
  • 155
  • 158