I'm trying to create a really simple node API using express.js 4 but I need a few 'realtime' events for which I added socket.io. I'm fairly new to both so I'm likely missing something basic but I can't find good docs/tuts on this.
In the express app (created with the express generator) I have something like this based on simple examples and project docs that I read. This works OK and from client apps, I can send/receive the socket events:
var express = require('express');
var path = require('path');
var logger = require('morgan');
var api = require('./routes/api');
var app = express();
var io = require('socket.io').listen(app.listen(3000));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/api', api);
io.sockets.on('connection', function (socket) {
console.log('client connect');
socket.on('echo', function (data) {
io.sockets.emit('message', data);
});
});
// error handlers omitted
module.exports = app;
but I want to use the sockets from my API routes (in the ./routes/api.js file that I 'require' above). For example, someone might use the API to PUT/POST a resource and I want that broadcast to connected socket.io clients.
I cannot see how to use the 'io' variable or organise the code currently in the io.sockets.on('connection' ...
function inside express routes. Here's the ./routes/api.js
file:
var express = require('express');
var router = express.Router();
var io = ???;
router.put('/foo', function(req, res) {
/*
do stuff to update the foo resource
...
*/
// now broadcast the updated foo..
io.sockets.emit('update', foo); // how?
});
module.exports = router;
We are going to use Express and Socket.IO side by side. Socket.IO does not use HTTP like a web application. It is event based, not request based. This means that Socket.IO will not interfere with Express routes that we have set up, and that's a good thing.
The express. Router() function is used to create a new router object. This function is used when you want to create a new router object in your program to handle requests. Multiple requests can be easily differentiated with the help of the Router() function in Express. js.
A route method is derived from one of the HTTP methods, and is attached to an instance of the express class. The following code is an example of routes that are defined for the GET and the POST methods to the root of the app. Express supports methods that correspond to all HTTP request methods: get , post , and so on.
One option is to pass it in to req object.
app.js:
var express = require('express'); var path = require('path'); var logger = require('morgan'); var api = require('./routes/api'); var app = express(); var io = require('socket.io').listen(app.listen(3000)); app.use(logger('dev')); app.use(express.static(path.join(__dirname, 'public'))); io.sockets.on('connection', function (socket) { console.log('client connect'); socket.on('echo', function (data) { io.sockets.emit('message', data); }); }); // Make io accessible to our router app.use(function(req,res,next){ req.io = io; next(); }); app.use('/api', api); // error handlers omitted module.exports = app;
./routes/api.js:
var express = require('express'); var router = express.Router(); router.put('/foo', function(req, res) { /* do stuff to update the foo resource ... */ // now broadcast the updated foo.. req.io.sockets.emit('update', foo); }); module.exports = router;
I've modified your files a little bit, may you check if it works?
You can pass the io you've defined to your routes like below;
require('./routes/api')(app,io);
I didn't test the Socket.IO parts but there is no syntax error and routes also working.
server.js file:
var express = require('express'); var app = express(); var path = require('path'); var logger = require('morgan'); var io = require('socket.io').listen(app.listen(3000)); app.use(logger('dev')); app.use(express.static(path.join(__dirname, 'public'))); io.sockets.on('connection', function (socket) { console.log('client connect'); socket.on('echo', function (data) { io.sockets.emit('message', data); }); }); require('./routes/api')(app,io); console.log("Server listening at port 3000");
api.js:
module.exports = function(app,io) { app.put('/foo', function(req, res) { /* do stuff to update the foo resource ... */ // now broadcast the updated foo.. console.log("PUT OK!"); io.sockets.emit('update'); // how? res.json({result: "update sent over IO"}); }); }
Supposing you want to access the SocketIO from anywhere in your application, not just in the router, you could create a singleton for it. This is what works for me:
//socket-singletion.js
var socket = require('socket.io');
var SocketSingleton = (function() {
this.io = null;
this.configure = function(server) {
this.io = socket(server);
}
return this;
})();
module.exports = SocketSingleton;
Then, you need to configure it using your server:
//server config file
var SocketSingleton = require('./socket-singleton');
var http = require('http');
var server = http.createServer(app);
SocketSingleton.configure(server); // <--here
server.listen('3000');
Finally, use it wherever you want:
//router/index.js
var express = require('express');
var router = express.Router();
var SocketSingleton = require('../socket-singleton');
/* GET home page. */
router.get('/', function(req, res, next) {
setTimeout(function(){
SocketSingleton.io.emit('news', {msg: 'success!'});
}, 3000);
res.render('index', { title: 'Express' });
});
module.exports = router;
One more option is to use req.app.
const express = require('express');
const path = require('path');
const logger = require('morgan');
const api = require('./routes/api');
const app = express();
const io = require('socket.io').listen(app.listen(3000));
// Keep the io instance
app.io = io;
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
// ...
app.use('/api', api);
module.exports = app;
const express = require('express');
const router = express.Router();
router.put('/foo', function(req, res) {
/*
* API
*/
// Broadcast the updated foo..
req.app.io.sockets.emit('update', foo);
});
module.exports = router;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With