6

I'm trying to get my head around ExpressJS and Socket.IO. I've got my routes in a separate file which I include from my app.js:

var express = require('express')    
  , db = require('./db')
  , mongoose = require('mongoose')
  , models = require('./models/device')
  , http = require('http')
  , path = require('path')
  , app = express()
  , server = http.createServer(app)
  , io = require('socket.io').listen(server)
  , routes = require('./routes/myRoutes');

However when I try and emit an event from one of my routes I have no reference to socket.io.

exports.update = function(req, res){
    return Item.findById(req.params.id, function(err, item) {
       // Do some checks and save.
       socket.emit('updated');
    }
}

I understand why this might not be available. Rather I don't understand what the best way to get a handle on socket.io is from another file other than app.js. I was looking at this question (see Ricardo's answer) but I'm still not clear. Ideally I would like to avoid doing this:

routes = requires("routes/myRoutes")(io);

Community
  • 1
  • 1
backdesk
  • 1,781
  • 3
  • 22
  • 42
  • you can avoid using the http module, although socket.io need a http server you can call it from the express dependency (3.x.x) thus: var app = express(), server = app.listen(3000), io = socket.listen(server); – Marco Godínez Dec 26 '12 at 02:43
  • Update: Take a look at feathers.js – backdesk Dec 10 '15 at 11:00

5 Answers5

9

Well you don't really need express.io for that. The easiest solution would be to export a new module and pass it a reference to socket.io instance. So in your app.js you have :

var express = require('express'),
  ...
  , server = http.createServer(app)
  , io = require('socket.io').listen(server)
  , routes = require('./routes/myRoutes');

Now require a new module and pass it the socketio reference. Add this new line (in app.js) to do so :

require('./app/path/to/your/socketio/controller/socketio')(io);

Then create a new file in your path/to/your/socketio/controller called socketio.js

And finally in the socketio.js file, export your new module :

module.exports = function(io) {

    io.sockets.on('connection', function (socket) {

        socket.on('captain', function(data) {

            console.log(data);

            socket.emit('america');
        });
    });
};

And there you go!

Kamagatos
  • 854
  • 9
  • 12
  • Thanks for the example. Could you elaborate further on not needing express? I've been thinking for a while about ways to implement a pure socket-based solution. – backdesk Apr 23 '14 at 10:35
  • 2
    This "sockets.js" module strategy looks pretty popular on Github, but for the life of me - I cannot understand how the routes file (e.g ./routes/myRoutes) will have access to that module(sockets.js) later. :( – Nick Pineda Feb 22 '16 at 06:18
5

Check out express.io

It has routing for realtime requests, and a bunch of other useful features.

app = require('express.io')()
app.http().io()

app.io.route('example', function(req) {
    // do something interesting
}))

app.listen(7076)

As far as getting around having to pass the io object around. You have a few options, which may or may not be "best" depending on who you ask.

  • Make io or app global. (some people freak out over globals)
  • Use module.exports and require the object in the other file. (can lead to circular dependency issues if not done properly)

Passing the io object is probably the cleanest simplest way, but you do have options.

Brad C
  • 720
  • 8
  • 14
2

socket.io doesn't work with routes, it works with sockets.

You add this code to app.js or a separate file which you include in app.js:

io.sockets.on('connection', function (socket) {
  socket.on('update', function (your_id) {
    Item.findById(your_id, function(err, item) {
      socket.emit('send_update', item);
    }
  });
});

Your update route only renders a html document with javascript, containing:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.emit('update', { your_id: '1' });
  socket.on('send_update', function (data) {
    console.log(data);
  });
</script>

So once the page is rendered and complete, the client-javascript will open a socket to the server and receive additional data.

See more examples here.

Disclaimer: Code written from scratch and not tested.

Patrick
  • 7,903
  • 11
  • 52
  • 87
  • Understood. My express app only acts as a restful API so there's not much going on view wise other than the default. I wanted to emit an event once a record had changed - just wasn't sure if I should send the notification from the server (once it's saved) or the client. – backdesk Sep 26 '12 at 06:24
  • You could do that with a .post-route in express and an [jQuery](http://api.jquery.com/change/)/[ajax](http://api.jquery.com/jQuery.post/) solution. Might be better than socket.io. – Patrick Sep 26 '12 at 10:23
2

The best way is to use closure. Ex:

exports.update = function(socket){
  return function(req, res) {
    //In here now you can have the reference to the socket io
    Item.findById(req.params.id, function(err, item) {
       // Do some checks and save.
       socket.emit('updated');
    }
  }
}

And:

app.post('/your_path', update(io));
Tuong Le
  • 18,533
  • 11
  • 50
  • 44
0

I've used the middleware router of Expressjs (I'm using 4.x).

In the external route file I put the "next" object:

module.exports = function(app, settings){
    app.post('/something', function(req, res, next) {
    ...
    next();
    }
};

And in the main file I write the last route hop inside the io.on('connection'):

io.on('connection', function (socket) {

    // Final route for middlewares who need to interact with socket.io
    app.post('/something', function (req, res, next) {
        socket.emit('event', { result: 'ok' });
    });

});
goliardico
  • 65
  • 8