A few months ago, I tried to start a Node project to further explore web development. It’s the LAN equivalent of a Jukebox: people go to the site, add music from Youtube or a file, and the song is put into a queue. The server has a speaker, through which it plays each song. The basic elements of this include a playlist and media buttons, which are constantly changing as people add music and change the state of the player. This is a job for sockets. I used the Express Generator to start myself off. This tool gives you 4 basic pieces:
bin/www
, which starts the HTTP server.app.js
, which starts Express, and connects to your routers.routes/*.js
, which handle and display information depending on the URI.views/*
, which display information to the browser.
Socket.io requires direct access to the HTTP server: otherwise, it can’t filter out socket requests from other requests (things that Express needs to route). Given that the HTTP server and Express starting were split up, this proposed a problem.
Originally, I fixed it by putting everything into one file. JavaScript is a mess, so why not solve a JavaScript problem with a mess, right? I couldn’t stand that for more than a few minutes, so off I went to learn how to pass my Socket.io object correctly. Turns out, there isn’t a fully functional, elegant way. But there is a way.
I came across a guide illustrating (what seemed like) exactly what I needed. I had to tweak it a bit. The process, in a nutshell, is:
- Move the starting of web server from
bin/www
toapp.js
. - Expose the web server inapp.js
, sobin/www
could have a reference to it. - Start Socket.io inapp.js
, and connect it to the web server. - Add the Socket.io reference to all routing requests, before they’re sent to the router:
app.use(function(req, res, next){
res.io = io;
next();
});
- Use the reference in get and post requests:
router.get('/', function(req, res, next) {
res.io.emit("socketToMe", "users");
res.send("respond with a resource.");
});
However, this emitting of signals is all that’s covered in the guide, and for a good reason: there’s no way to listen to a socket inside these requests. I needed that to handle events such as Play/Pause button presses.
The workaround:
- Instead of adding
io
to your res object, put all ofroutes/*.js
inside functions. Instead of an exports line at the bottom, return what you’d like to export:
function (io) {
// Require statements
// get and post statements
...
return router;
}
- From
app.js
, call all your route files with an argument including your Socket.io reference:
var routes = require("./routes/index")(io);
So far, this seems to work for a little demonstration.