Socket.io Middle-ware - node.js

I am wondering how can i make socket.io avail outside my app.js page. I have currently all my socket related code on the app.js page after
const express = require('express');
const app = express();
const socketio = require('socket.io
const expressServer = app.listen(9999);
const io = socketio(expressServer);
where i can use the io.xxx but what i rather would do is to initialize the socket.io and then put all related code into a separate file where i can then expose functions to call emits etc.

You can put the socket.io code in it's own module and use exports to initialize and share the instance.
sio.js
const socketio = require('socket.io');
let io;
module.exports = {
init: function(server) {
if (io) {
throw new Error("socket.io already initialized");
}
// initalize socket.io to this server
io = socketio(server);
// put other socket.io initialization code here
return io;
}
get: function() {
if (!io) {
throw new Error("socket.io has not yet been initialized");
}
return io;
}
}
app.js
const express = require('express');
const app = express();
const expressServer = app.listen(9999);
const io = require('sio').init(expressServer);
some other module file that wants access to socket.io instance
const io = require('sio').get();
This allows one socket.io instance bound to one server per process (because the io instance is stored in module data). It could be extended to support multiple instances for multiple servers, but you'd then have to say which server you wanted the instance for when requesting the instance.
Note also that the .init() method must be called before the .get() can be called so the sio module should be loaded and call .init() on it early in the app module's setup before it loads other things that themselves might want to load sio.

Related

Making Socket and rest to work together using nodejs

Currently I am using "Socket.io" package in my node application and I want to access IO instance in my rest calls.
Following is code I had tried.
index.js
const app = express();
const newServer = require('http').Server(app);
const { Server } = require('socket.io');
const io = new Server(newServer);
app.use("/restApiCall", require("./apis"));
newServer.listen(8002, () => {
console.log(`Server listening to port 8002`);
});
apis.js
const express = require("express");
const router = express.Router();
const SubmoduleController = require("./controllers/subModule.js");
router.route('/abc').post(SubmoduleController.updateAbc);
subModule.js
module.exports = {
updateAbc: async function (req, res, next) {
/*** Here i want access my socket IO instance and emit message to UI
}
}
How do I achieve this. Since IO instance is part of index.js and rest api are written in different files. How do I Pass instance?
If you want to use both socket.io and express server (for REST API), you should listen on 2 different port.

Implement socket.io in node.js application controller

good afternoon. I am new to programming sockets in node.js and I need to implement socket.io in a controller of my application. The architecture I have is the following:
The file that starts the server is index.js
const express = require('express');
const app = express();
const port = 3000;
const socketRouter = require('./routes/socket')
app.use(express.json());
//Route
app.use('/socket', socketRouter);
app.listen(port, () => {
console.log(`Server connection on http://127.0.0.1:${port}`); // Server Connnected
});
The file where I define the routes is socket.js
const { Router } = require('express');
const { showData } = require('../controllers/socket');
const router = Router();
router.post('/send-notification', showData);
module.exports = router;
And my controller is:
const { response } = require('express');
const showData = (req, res = response) => {
const notify = { data: req.body };
//socket.emit('notification', notify); // Updates Live Notification
res.send(notify);
}
module.exports={
showData
}
I need to implement socket.io in this controller to be able to emit from it but I can't get it to work. Could you tell me how to do it?
Thanks a lot
CLARIFICATION: if I implement socket.io in the main file it works, but I want to have some order and separate things. This is how it works:
const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
app.post('/send-notification', (req, res) => {
const notify = { data: req.body };
socket.emit('notification', notify); // Updates Live Notification
res.send(notify);
});
const server = app.listen(port, () => {
console.log(`Server connection on http://127.0.0.1:${port}`); // Server Connnected
});
const socket = require('socket.io')(server);
socket.on('connection', socket => {
console.log('Socket: client connected');
});
Move your socket.io code to its own module where you can export a method that shares the socket.io server instance:
// local socketio.js module
const socketio = require('socket.io');
let io;
modules.exports = {
init: function(server) {
io = socketio(server);
return io;
},
getIO: function() {
if (!io) {
throw new Error("Can't get io instance before calling .init()");
}
return io;
}
}
Then, initialize the socketio.js module in your main app file:
const express = require('express');
const app = express();
const port = 3000;
app.use(express.json());
const server = app.listen(port, () => {
console.log(`Server connection on http://127.0.0.1:${port}`); // Server Connnected
});
// initialize your local socket.io module
const sio = require('./socketio.js');
sio.init(server);
// now load socket.io dependent routes
// only after .init() has been called on socket.io module
const socketRouter = require('./routes/socket')
app.use('/socket', socketRouter);
Then, anywhere you want to access the socket.io server instance, you can
require("./socketio.js") and use the .getIO() method to get the socket.io instance:
// use correct path to socketio.js depending upon where this module
// is located in the file system
const io = require("../../socketio.js").getIO();
// some Express route in a controller
const showData = (req, res) => {
const notify = { data: req.body };
// send notification to all connected clients
io.emit('notification', notify);
res.send(notify);
};
module.exports= {
showData
};
Note: A typical socket.io usage convention on the server is to use io as the server instance and socket as an individual client connection socket instance. Please don't try to use socket for both. This makes it clear that io.emit(...) is attempting to send to all connected clients and socket.emit() is attempting to send to a single connected client.
Also note that if your route is triggered by a form post where the browser itself sends the form post, then that particular client will not receive the results of io.emit(...) done from that form post route because that browser will be in the process of loading a new web page based on the response of the form post and will be destroying its current socket.io connection. If the form post is done entirely via Javascript using an Ajax call, then that webpage will stay active and will receive the results of the io.emit(...).
You can use the same socket and app (if you need to expose APIs as well) in other files if you want to separate socket messages and REST endpoints by functionality or however you choose to organize it. Here's an example of how this can be done:
Create a new file, let's say controller1.js:
function initialize(socket, app) {
socket.on('some-socket-message', socket => {
// Whatever you want to do
});
app.get('/some-endpoint', (req, res) => {
// whatever you want to do
});
}
module.exports = {initialize}
And then add the following to your controller.js
const controller1 = require('path/to/controller1');
...
// At some point after socket and app have been defined
controller1.initalize(socket, app);
This will be the bases of separating your controller however you want, while still using the same socket connection and API port in all of your controllers. You can also refactor the initialize method into different methods, but that would be at your own discretion and how you want to name functions, etc. It also does not need to be called initalize, that was just my name of preference.

How to deal with socket.io being undefined in app.js

My express app logic is separated into a separate file from the instantiation of the express server, so I'm having issues with accessing socket.io within this app file. Should I just move the socket.io implementation into index.js or is it possible to keep that logic in app.js?
index.js
const http = require('http');
const app = require('./app');
const server = http.createServer(app);
const io = require('socket.io')(server);
const config = require('./utils/config');
const logger = require('./utils/logger');
app.set('socketio', io);
server.listen(config.PORT, () => {
logger.info(`Listening on port ${config.PORT}`);
});
app.js
const express = require('express');
const config = require('./utils/config');
const middleware = require('./utils/middleware');
const app = express();
app.use(middleware.requestLogger);
const io = app.get('socketio');
io.on('connection', (socket) => {
io.emit('test', { test: 'test' });
});
app.use(middleware.errorHandler);
module.exports = app;
You have a load order problem. You are loading app.js into index.js BEFORE you create and set io as an app property. So, when app.js tries to use the io property, it hasn't yet been set.
The way you have things split between the files, you've created a circular dependency. You can't create io until you've created the server, but you can't create the server until you have the app which is in app.js. So, you can't create io before you load app.js.
There are lots of ways around this. I find it kind of weird that you're creating the server in one file and the app object in another file since the two are fully required to make an operational server. So, I'd rearrange how those things are done like this:
// index.js
const http = require('http');
const app = require('express')();
const server = http.createServer(app);
const io = require('socket.io')(server);
app.set('socketio', io);
require('./app.js')(app);
const config = require('./utils/config');
const logger = require('./utils/logger');
server.listen(config.PORT, () => {
logger.info(`Listening on port ${config.PORT}`);
});
And, then modify app.js like this:
const config = require('./utils/config');
const middleware = require('./utils/middleware');
module.exports = function(app) {
app.use(middleware.requestLogger);
const io = app.get('socketio');
io.on('connection', (socket) => {
io.emit('test', { test: 'test' });
});
app.use(middleware.errorHandler);
}
There are 100 other ways to organize the code to fix this. Exporting a function that you can call and passing that function one or more arguments is one way to help control the timing of these circular dependencies.

Should I use a global variable to share socket.io instance across entire server

The following is my server.js file in my node.js application. I want my socket.io instance to be accessed by other files on my server in order to emit events from my API (listingRoutesApi, userRoutesApi etc.) (refer to code).
The problem I have is that my routes are declared before the server is created; however, the socket.io instance is created after the server is created.
The solution I've used is to declare a global io variable that will allow me to emit events from anywhere within my web app like so:
global.io.of('/analytics').to(listing._id).emit('message', "There was a post.");
My question is: are there any pitfalls / dangers from doing this and will I encounter any scalability issues in the long-term? Additionally, is there a better way of achieving my objective?
Code within my server.js file:
const app = express();
app.use('/api', listingRoutesApi);
app.use('/api', userRoutesApi);
app.use('/api', imageRoutesApi);
// ...plenty more endpoints here...
app.use(serveStatic(path.join(__dirname, "/dist")));
app.use(history());
app.use(serveStatic(path.join(__dirname, "/dist")));
const server = app.listen(port, () => { console.log('server started ' + port); });
/* Start socket. */
global.io = socketio(server);
const analytics = global.io.of("/analytics");
analytics.on('connection', (socket) => {
socket.on('join', (data) => {
socket.join(data.room);
analytics.in(data.room).emit('message', `New user joined ${data.room}`);
});
socket.on('leave', (data) => {
analytics.in(data.room).emit('message', `User leaving ${data.room}`);
socket.leave(data.room);
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
I'm asking this question since this SO post answers a similar question by declaring a getIOInstance function and passes it to all modules that require it. While, it works, it doesn't feel very elegant and seems a little unnecessary given I expect to only ever have exactly one socket.io instance in my application.
Additionally, I would assume the challenge I'm having is a very common one; however, I haven't been able to find many solutions to address it and none that suggest using a global variable.
Node.js is a modular environment. Modules are suppose to address some flaws that globals have.
Modules naturally provide singleton instances, in case there's a need to have only one instance:
app.js
module.export = express();
server.js
const app = require('./app');
// can go to app.js if configured but unlistened app is needed for reusability or testing
app.use(/* router */);
module.export = app.listen(...);
socketio.js
const server = require('./server');
module.export = socketio(server);
index.js
const app = require('./app');
const io = require('./socketio');
...
Express also provides a container for application global dependencies, application settings table. It can be used everywhere application instance is available, e.g. as req.app.get(...) inside middlewares. Accessing Express application instance outside middlewares won't be a problem if it's a singleton as well:
app.js
module.export = express();
index.js
const app = require('./app');
app.use(/* router */);
...
const server = app.listen(...);
const io = socketio(server);
app.set('io', io);
// available as req.app.get('io') inside middlewares
// and as require('./app').get('io') outside them

Issue exporting `express()` and `ws()` objects to other files in Node?

Let's say the entry point of my app is app.js
I want to create a socket connect when the app runs and then export it to other files throughout the application.
Here is a bit of example code
const express = require('express');
const WebSocket = require('ws');
const app = express();
app.use(allTheMiddleWares);
app.listen(PORT, () => {
console.log('started app');
}
const socket = new WebSocket.Server({server: app});
module.exports = socket;
Then when I try to access the export from app.js I always end up with an empty object.
I have also tried exporting functions:
module.exports = {
test: () => console.log("why don't I work")
}
However this also returns an empty object.
Thanks in advance for the help.
As a temporary work around to access the socket globally I have set process.WebSocket = new WebSocket.Server({server: app});. I would like to know if there are any glaring issues with this.
Making my comment into an answer since it was your solution.
You are not passing a server to the WebSocket constructor. app is a request handler. It is not a server. app.listen() returns a newly created server object.
Change your code from this:
app.listen(PORT, () => {
console.log('started app');
}
const socket = new WebSocket.Server({server: app});
to this:
const server = app.listen(PORT, () => {
console.log('started app');
}
const socket = new WebSocket.Server({server: server});
See code for app.listen() here.
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
See doc for app.listen() here.
The app.listen() method returns an http.Server object and (for HTTP) is a convenience method for the following:

Resources