How to create a reusable Socket.IO module

I'm having trouble creating a module that provides functions for my Socket.IO library:

const sio = require('socket.io'); module.exports = function(server) { const io = sio(server); return { register: function(namespace) { let nsp = io.of(namespace); nsp.on('connect', function(socket) { // ... } } } } 

The problem is, how can I use this in other modules? In my app.js

I create a server using Express and can create an instance of a module using require('./mysocketio')(server) , but not in other modules, because the server is not available there. What is a good way to solve these circular dependencies?

+6
source share
4 answers

Well, you can achieve this in many ways, for example:

  • setting objects in the global namespace. (changing needs in global needs)
  • Use module.exports and claim the object in other files. (can lead to issues with circular addiction if they are not performed properly)
  • pass the instance as arguments to the controllers, requiring them in the routes.

myModule.js A module that provides the functionality of your Socket.IO library

 const sio = require('socket.io'); module.exports = function(server) { const io = sio(server); return { register: function(namespace) { let nsp = io.of(namespace); nsp.on('connect', function(socket) { // ... } } } } 

FLOW 1: Install the module in the global namespace.

app.js

 var app = require('express').createServer(); var io = require('./myModule')(app); global._io = io; app.listen(80) 

controller.js

 module.exports = function(io){ var that={}; /* * Private local variable * made const so that * one does not alter it by mistake * later on. */ const _io = global._io; that.myAction = function(req,res){ _io.register('newRoom'); res.send('Done'); } return that; } 

Protocol 2: passing the module as arguments.

app.js

 var app = require('express').createServer(); var io = require('./myModule')(app); require(./router.js)(app,io); app.listen(80); 

router.js

 /* * Contains the routing logic */ module.exports = function (app,io) { //passing while creating the instance of controller for the first time. var controller = require("./controller")(io); app.get('/test/about',controller.myAction); }; 

controller.js

 module.exports = function(io){ var that={}; const _io = io; that.myAction = function(req,res){ _io.register('newsRoom'); res.send('Done'); } // everything attached to that will be exposed // more like making public member functions and properties. return that; } 

Stream 3: Installing io to the global. Thus, you do not need to transfer the server every time.

app.js

 var app = require('express').createServer(); require('./myModule')(app); require(./router.js)(app); app.listen(80); 

controller.js

 // no need to pass the server as io is already initialized const _io = require('./myModule')(); module.exports = function(io){ var that={}; that.myAction = function(req,res){ _io.register('newsRoom'); res.send('Done'); } return that; } 

myModule.js

 module.exports = function( server ) { const _io = global._io || require('socket.io')(server); if(global._io === undefined){ //initializing io for future use global._io = _io; } return { register: function(namespace) { let nsp = _io.of(namespace); nsp.on('connect', function(socket) { // ... } } } } 

Probably the cleanest way is to pass arguments to the controllers, requiring them in routes. Although the third thread seems promising, care must be taken when changing the global namespace.

+2
source

This is not a circular addiction; It is just that your module a) depends on another module that is not globally accessible, and b) your module seems to be used in many places in your code.

Global

A possible solution (with drawbacks) is to simply load your module once and attach it to the global one: global.mysocketio = require('./mysocketio')(server);

This allows you to access global.mysocketio anywhere in your project after downloading it. This is a design that I personally use for my own construction of a logger; My logger is used in many places around my code, so I just bind it to global.log.

However, using globals is a bit dirty; This gives problems with the separation of the namespace (that somewhere, some code decides to use global.mysocketio itself), and creates an β€œinvisible” dependency; Another code simply assumes that a certain global one will exist, and it is not so easy to find these dependencies.

Export

The best solution is to simply pass the variable where necessary. There are many ways to do this. I understand that your app.js does not have an available server variable, but it certainly includes your express code. If you need a "server" or "mysocketio" accessible from app.js, just export it from your module, where you create a "server". How:

module.exports.expressServerVar = server;

Only my 2 cents; Do you strongly disagree with me, or did I miss something important? Let me know!

+1
source

I would use factory injection or dependencies. You can use something like jimple .

But here is an example without any external dependencies. This is by no means the best example of code, but it should hopefully make sense. I would still recommend using jimple, not that.

 // app.js var express = require('express'); var app = express(); var factory = require('./factory.js'); factory.setExpress(app); // This could also be done in the factory constructor. Or you could instanciate your express app in the factory.js class. // factory.js var socketIoModule = require('./your-socket-io-module.js') function Factory() { } Factory.prototype.setExpress = function(app) { this.app = app; } Factory.prototype.getSocketIOModule = function() { return socketIoModule(this.app); } // By exporting it this way we are making it a singleton // This means that each module that requires this file will // get the same instance of factory. module.exports = new Factory(); // some code that needs socket io module var factory = require('./factory.js'); function() { var socketIo = factory.getSocketIOModule(); socketIo.doStuff(); } 
+1
source

The approach I use in my applications is to expose the server and io instances from the beginning of the script and reuse them in the modules

 // Setup servers. var http = require('http').Server(app); var io = require('socket.io')(http); // Setup application. require('./server/app')(app, express, io); // Start listening on port. http.listen(configs.PORT, function() { console.log("Listening on " + configs.PORT); }); 

Inside your modules, you can use an io instance to install event handlers or emit events, something like this

 module.exports = { contest: function(io, contest) { var namespace = io.of('/' + contest.id); namespace.on('connection', function(socket) { socket.on('word', function(data) { ... }); }); } }; 

For your sample

I would put this part in app.js or in the js file that is used to start the server

 const sio = require('socket.io'); const io = sio(server); 

and will have a Socket.IO module like this

 module.exports = function(server, io) { return { register: function(namespace) { let nsp = io.of(namespace); nsp.on('connect', function(socket) { // ... } } } } 

My example

+1
source

All Articles