Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I emit events to connected sockets using socket.io from within my Express 4 routes?

This is a question other people have asked, but I can't manage to benefit from the answers they've been given, due to the different Express setup I have.

I've got socket.io implemented and working in a simple way on my server. This is how it works:

In bin/www:

#!/usr/bin/env node
var debug = require('debug')('gokibitz');
var app = require('../../server');

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() {
    debug('Express server listening on port ' + server.address().port);
});

var io = require('socket.io').listen(server);

io.on('connection', require('../routes/socket.js'));

All my routes and other Express setup is in ../../server.js.

var routes = require('./server/routes/index');
var user = require('./server/routes/user');
...

app.use('/', routes);
app.use('/api/user/', user);
...
app.use('*', routes);

Then in ../routes/socket.js, I've got this boilerplate:

module.exports = function (socket) {
    socket.emit('send:news', { hello: 'world' });

    setInterval(function () {
        socket.emit('send:time', {
            time: (new Date()).toString()
        });
    }, 1000);

    return socket;
};

This is working beautifully, I might add. But now I want to be able to emit events from the various routes in my quite-ordinary Express app, and I can't for the life of me figure out the right way to get a reference to the socket object I need.

Example: when a user makes a comment, I'd like to emit an event to all connected users notifying them of the new comment. From my routes file (./server/routes/user.js), how can I get access to the object I need to emit events?

Here's a skeleton of the relevant bits from a route file:

var express = require('express');
var router = express.Router();

router.post('/', function (req, res) {
  ...
});

module.exports = router;

The only place I can access it is in the ../routes/socket.js file, which is useless.

All of the routes are set in a app.js before there's any io or socket object to pass in.

Should I be able to require('socket.io') and use it somehow to emit to all connected sockets?

Is there a sensible way to store the connected sockets on ../routes/socket.js so it can be required and emitted to from other routes?

Can anyone steer me in the right direction?

like image 869
Nate Avatar asked Feb 02 '15 21:02

Nate


3 Answers

I was able to ultimately get things working using this example: https://github.com/expressjs/generator/issues/45#issuecomment-53719435

I created a new file called server/io.js:

var io = require('socket.io')();

io.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

module.exports = io;

Then I updated server/bin/www to this:

#!/usr/bin/env node
var debug = require('debug')('gokibitz');
var app = require('../../server');
var io = require('../io');

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() {
    debug('Express server listening on port ' + server.address().port);
});

io.attach(server);

Then, in my route, I use this:

var io = require('../io');
...
io.emit(...);

The missing piece for me, at least, was the ability to create the io object separately, return the correct object, then use io.attach(server) inside bin/www to start it up at the right point.

Hopefully this will help someone else following in my footsteps.

like image 97
Nate Avatar answered Sep 18 '22 13:09

Nate


I think you are confused with the concepts. The socket.io lib uses or emulate a websocket (bidirectional socket) with the client, and have not relation with routes.

You can send a notification to all sockets using the io object:

io.emit('message_to_all', true);

You have to an array on io.sockets, with all sockets.

You can uses namespaces or rooms to, I recomend you learn the documentation:

http://socket.io/docs/rooms-and-namespaces/#

Add something:

If you want to send a message to all people in the same route, you can join to a channel with the same name of the path.

For example, in the client:

var socket = io(window.location.href); // Or route..

And the server:

var nsp = io.of('/the/specific/route');
nsp.on('connection', function(socket){
  // ...
});
nsp.emit('message_to_all_in_route', data);

About the last question editing:

You can send the io object in request or response object to routes, using the Express midleware API:

For example:

#!/usr/bin/env node
var debug = require('debug')('gokibitz');
var app = require('../../server');

app.set('port', process.env.PORT || 3000);

var server = app.listen(app.get('port'), function() {
    debug('Express server listening on port ' + server.address().port);
});

var io = require('socket.io').listen(server);

app.use(function (req, res, next) {
  res.io = io;
  next();
});

io.on('connection', require('../routes/socket.js'));

And in the route:

route.post('/post/:id/comments', function (req, res) {

   // Do your logic

  var postio = res.io.of('/post/' + req.params.id);
  postio.emit('new_comment', commentData);

});
like image 31
Exos Avatar answered Sep 19 '22 13:09

Exos


As I said in my comment, you can send to all connected clients with:

io.emit(msg, data);

This will require access to the io object that you created in your first module. The usual way to share that with your module with your routes modeule would be to export a method from the routes module that lets you pass it the io object and then after you've required in the routes module and created the io object, you can just call that method to pass the io object to the routes module and it can save it locally for future use.

io is just a shared object like the app object is. If you want a module to be able to use a shared object, the usual way is that you call some method in that module to share with it some objects that it can then use.

like image 26
jfriend00 Avatar answered Sep 18 '22 13:09

jfriend00