… so this is that post for modularizing apps that rely largely on socket.io events.
This is for the ones that believe on the single-reponsibility principle and don’t find an example for this when trying to fit socket.io in an app that starts demanding some structure.
tl;dr You can create socket.io middleware functions and export them from separate js files like you would export Express middleware.
Do I need structure at all ?
Something that kept happening to me over and over: I have the latest idea on a realtime feature for an app… I start by including socket.io as a module for my NodeJS app and as a library for my client side code (The usual /socket.io/socket.io.js).
…I create the functionality and when I finish implementing the new realtime feature… BAM! …I find myself inside unexplainable spaghetti code due to my own anxiety about socket.io’s simplicity and easiness of implementation.
The usual way for creating the first socket.io’s event handlers on the server one may find on every socket.io example around there makes use of the io.on(‘connection’) structure. Something like:
It works, so…. What’s wrong?
The issue I see now with examples that still show this approach on the server side is that the examples themselves don’t enforce any modular approach on the reader. And I mean, modular approaches that the NodeJS world is already used to.
What’s the issue again?
- What if I’d like to have several files, defining each one a specific set of socket.io events handlers that do not depend on the main file or each other?
- What if I’d also like no to clutter the main app/server file with lots of `require()` that are used on some specific event handlers and not on others?
- What if I’d like to write unit tests for each file module that run independently of other tests?
Introducing socket.io middleware
You can get the same you get from the io.on(‘connection’) pattern by using socket.io middleware functions.
[gist https://gist.github.com/oskosk/d4f1a4da6e6db46d9477 /]
It looks almost the same right?
Yes, BUT this approach lets us think in modularization the way Express users have come to think about. With time, patterns of file structure evolved for modularizing route handling on an Express based app.
Exported middleware structure
In this pattern, modules export a middleware function. A function that I can use() on a socket.io server instance.
[gist https://gist.github.com/oskosk/479ce5a0fa698a81ab01 /]
Exported high-order function structure
In this kind of file structure, the app specific modules export a function that return a middleware function. This returned function is what I can use() on the socket.io server instance.
This is useful for being able to pass the module specific data from the main file. Data that will need to be evaludated by the returns middleware function.
[gist https://gist.github.com/oskosk/ad186e0bf427ff175e83 /]
What can be done from the bottom up to organize code?
- Find a usual directory/file structure followed i.e. by other nodejs package mantainers.
- Use and abuse socket.io’s middleware capabilities provided by socket.io 1.x.
- Decide if you want your modules to export plain middleware or high order functions that return middlewares.
Example directory structure
This example structure is just here because I started writing this post with a patronizing tone and then evolved the other way around.
. ├── node_modules ├── package.json ├── lib │ ├── module1.js │ └── module2.js ├── public │ └── index.html └── server.js
- server.js — Creates the http/https server, attaches socket.io, requires lib modules.
- lib/module1.js lib/module2.js — export middleware or high order function that return middlewares. Require specific modules for a specific pack of event handlers. Can be unit tested on their own.
- public/*html — HTML + script tag loading /socket.io/socket.io.js.
Some references from others on these thoughts
- Best approach for a modular node.js/socket.io/express application (StackOverflow)
- architecture for modular socket.io in node.js (StackOverflow)
- How to modularize socket.io to multiple pages (StackOverflow)