I need access to the io module in multiple files in my project so I can emit some messages. So for that I decided to create a singleton class to handle the io object so i can import the class anywhere and use the io object.
When I comment out all the middleware, it works. But I don't want that, I want the connection to be only made when the user is logged in.
here's my socketClass.js
const session = require('express-session');
const { Server } = require("socket.io");
const passport = require('passport');
class SocketManager{
constructor() {
this.io = null;
}
start(server) {
this.io = new Server(server);
// convert a connect middleware to a Socket.IO middleware
const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);
const sessionMiddleware = session({
secret: 'MySecrectKey',
resave: true,
saveUninitialized: true
});
this.io.use(wrap(sessionMiddleware));
this.io.use(wrap(passport.initialize()));
this.io.use(wrap(passport.session()));
this.io.use((socket, next) => {
if (socket.request.user) {
next();
} else {
next(new Error('unauthorized'))
}
});
}
get() {
return this.io;
}
}
module.exports = new SocketManager();
Here's my app.js
...
const SocketManager = require('./socketClass');
const server = http.createServer(app);
SocketManager.start(server);
const io = SocketManager.get();
io.on('connect', async (socket) => {
console.log(`new connection ${socket.id}`);
socket.on('whoami', (cb) => {
cb(socket.request.user ? socket.request.user.username : '');
});
});
The io.on('connect') wont console.log anything on a new connection. What is the problem here?
Your problem may be because of "async (socket)". Here is how I do it as simply as possible !
I created a separate file (module):
socket.js:
let io;
module.exports = {
init: httpServer => {
io = require('socket.io')(httpServer);
return io;
},
getIO: () => {
if (!io) {
throw new Error('Socket.io not initialized!');
}
return io;
}
};
Then, in my app.js:
const app = express();
const server = app.listen(8080);
const io = require('./socket').init(server);
io.on('connection', socket => {
console.log('Client connected');
});
Related
In Nodejs how to export socket IO in controller.
Socket.io Version - "socket.io": "^4.5.1",
Socket.js
let io = null;
// module.exports = {
// intialized_connection: (httpServer) => {
// return (io = require('socket.io')(httpServer, {
// cors: {
// origin: '*',
// methods: ['GET', 'POST', 'PUT', 'DELETE'],
// },
// }));
// },
// getIO: () => {
// if (!io) {
// throw new Error('Socket.io is not initialized');
// }
// return io;
// }
// }
class RealTime {
constructor() {
if (io) return io;
io = this;
return io;
}
intialized_connection(httpServer) {
return (io = require('socket.io')(httpServer, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
}));
}
init() {
io.on("connection", function (socket) {
console.log("A user connected", socket.id);
//Whenever someone disconnects this piece of code executed
// socket.on('custom-event', function(data) {
// console.log("Atique data: ", JSON.stringify(data));
// });
// socket.emit('custom-emit', "hello from nodejs")
socket.on('disconnect', function () {
console.log('A user disconnected');
});
});
}
getIO() {
if (!io) {
throw new Error('Socket.io is not initialized');
}
return io;
}
sendEvents(event, data) {
console.log("This.Socket:", this.socket);
return new Promise((resolve, reject) => {
this.getIO().emit(event, data, (response) => {
if (response.error) {
console.error(response.error);
reject(response.error);
} else {
resolve(true);
}
});
});
}
receivedEvents(event) {
console.log("Atique Ahmed Received Events ---->", event);
return new Promise((resolve, reject) => {
this.getIO().on(event, function(err, data) {
console.log("I am emiiting here, ")
if(err) {
reject(err);
}
resolve(data);
});
})
}
}
module.exports = {
RealTime
};
index.js
const express = require('express');
const bodyparser = require('body-parser');
const cors = require('cors');
const fileUpload = require('express-fileupload');
const http = require('http');
// const socketIO = require('./utils/socket');
const { RealTime } = require('./utils/socket');
const socket = new RealTime();
const app = express();
app.use(cors())
app.options('*', cors());
app.use(bodyparser.json({limit: '5mb', extended: true}))
app.use(bodyparser.urlencoded({limit: '5mb', extended: true}))
const authRoutes = require('./routes/authRoutes');
const apiRoutes = require('./routes/routes');
// For File Upload
app.use(fileUpload({
limits: { fileSize: 5 * 1024 * 1024 },
}));
app.use('/auth', authRoutes);
app.use('/user', apiRoutes);
//Capture All 404 errors
app.use(function (req,res,next){
res.status(404).send('Error - Unable to find the requested resource!');
});
app.use((req, res, next) => {
req.socket.on('error', () => {});
next();
});
const server = http.createServer(app);
socket.intialized_connection(server);
socket.init();
app.set('socketio', socket);//here you export my socket.io to a global
module.exports = server;
local.js
require('dotenv').config()
const server = require('./index');
const port = process.env.PORT || 8081;
const chalk = require('chalk');
// Server
server.listen(port, () => {
console.log(chalk.green('╔═══════════════════════════════════════════════════════════'));
console.log(chalk.green('║ Background Server Listening at | port: %s', port));
console.log(chalk.green('╚═══════════════════════════════════════════════════════════'));
});
Routes.js
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
// Authentication
const authentication = require('../authentication');
// Middleware
const middleware = require('../middleware/headerValidation');
// Permission
const permissions = require('../permission/index')
// Controller
const userController = require('../controllers/userController');
const customerController = require('../controllers/customerController');
app.post('/submit-request', [middleware.bearerTokenPresent, authentication.verifyToken, permissions.fileUploadPermission], (req, res) => {
customerController.submitRequest(req, res);
});
module.exports = app;
customerController.js
exports.submitRequest = async(req, res) => {
const io = req.app.get('socketio');
io.emit('custom-emit', "Hello from nodejs");
io.on('custom-event', function(data) {
console.log("Atique:", JSON.stringify(data))
})
res.json("done")
}
Issue are -
The main issues are - socketio.on an socketio.emit is not working. It is working when I am putting everything, inside init method, I can't do that, I have to write the generic code, so it can be re-usable. -
init() {
io.on("connection", function (socket) {
console.log("A user connected", socket.id);
socket.on('custom-event', function(data) {
console.log("Atique data: ", JSON.stringify(data));
});
socket.emit('custom-emit', "hello from nodejs")
socket.on('disconnect', function () {
console.log('A user disconnected');
});
});
}
Frontend Angular 14, I am using, socket.io-client
Any idea, What I am doing wrong?
The object stored in your globals
app.set('socketio', socket);//here you export my socket.io to a global
is an instance of RealTime class and not of the require('socket.io').Server class.
please cache the reference to the proper object like so:
app.set('socketio',socket.intialized_connection(server));//here you export my socket.io to a global
socket.init();
change customerController.js:
exports.submitRequest = async (req, res) => {
const io = req.app.get('socketio');
///edited from io.on("connection", function (socket) {
io.once("connection", function (socket) {
socket.emit('custom-emit', "Hello from nodejs");
socket.on('custom-event', function (data) {
console.log("Atique:", JSON.stringify(data))
})
res.json("done")
});
}
you however have to keep in mind what listeners you are adding to the io object's "connection" or some other event as No checks are made to see if the listener has already been added. Multiple calls passing the same combination of "connection" and listener will result in the listener being added, and called, multiple times.
Consider using named functions and clearing the listener using removeListener() from time to time.
It is best to keep all your socket event listeners in one file for ease of debugging.
EDIT 1
index.js:
const server = http.createServer(app);
socket.intialized_connection(server);
socket.init();
app.set('socketio', socket);//here you export my socket.io to a global
make the following changes to your socket.js:
let io = null;
/// CHANGE:
let socketID = null;
class RealTime {
constructor() {
if (io) return io;
io = this;
return io;
}
intialized_connection(httpServer) {
return (io = require('socket.io')(httpServer, {
cors: {
origin: '*',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
},
}));
}
init() {
io.on("connection", function (socket) {
console.log("A user connected", socket.id);
/// CHANGE:
socketID = socket.id
//Whenever someone disconnects this piece of code executed
// socket.on('custom-event', function(data) {
// console.log("Atique data: ", JSON.stringify(data));
// });
// socket.emit('custom-emit', "hello from nodejs")
socket.on('disconnect', function () {
console.log('A user disconnected');
});
});
}
/// CHANGE: getIO() {
getSocket() {
if (!io) {
throw new Error('Socket.io is not initialized');
}
return io.sockets.sockets.get(socketID);
}
.
.
.
.
for the receivedEvents and sendEvents to get reference to the socket.
Since you are caching the socket id here, this code will work only for one client properly.
change customerController.js:
exports.submitRequest = async (req, res) => {
const socket = req.app.get('socketio').getSocket();
socket.emit('custom-emit', "Hello from nodejs");
socket.on('custom-event', function (data) {
console.log("Atique:", JSON.stringify(data))
})
res.json("done")
}
I'm relatively new to node.js and I'm trying to include socket.io into a controller. The idea is to respond to a client when an order is placed through the response object of express but in addition I'd also like to emit an event so that the restaurant owner sees the orders from all the customers coming in 'live'.
I have an index.js file in an api folder with the following code, where I export api, server and PORT:
`
const express = require('express');
const morgan = require('morgan');
const http = require('http');
const cors = require('cors');
const api = express();
const server = http.createServer(api);
const PORT = process.env.PORT || 3000;
api.use(cors());
api.use(morgan('common'));
api.use(express.urlencoded({ extended: true }));
api.use(express.json({ extended: true }));
api.use('/api/v1', require('../routers'));
api.get('/', (req, res) => {
res.send('Backend running.');
});
module.exports = { api, server, PORT };
In the root of the project I have another index.js file with the following code:
/* eslint-disable no-console */
require('dotenv').config();
const mongoose = require('mongoose');
const { api, server, PORT } = require('./api');
const { MONGO_URI } = require('./config');
mongoose.connect(
MONGO_URI,
{ useNewUrlParser: true, useUnifiedTopology: true },
)
.then(() => console.log('Connected to DB'))
.catch((err) => console.log('Error occured while trying to connect to DB', err));
api.listen(PORT, () => console.log(`Listening on ${PORT}`));
const io = require('./socket').init(server);
io.on('connection', (socket) => {
console.log('Connection success', socket.id);
socket.on('disconnect', () => {
console.log('Connection disconnected', socket.id);
});
});
I've placed the code to initialize socket.io and get an instance of it in a folder named socket with the following code in the index.js file:
/* eslint-disable consistent-return */
/* eslint-disable global-require */
const { Server } = require('socket.io');
let io;
module.exports = {
init: (server) => {
try {
io = new Server(server);
return io;
} catch (err) {
console.log(err);
}
},
get: () => {
if (!io) {
throw new Error('socket is not initialized');
}
return io;
},
};
Then I import the io instance in a controller but when I emit an event I get the error that the socket is not initialized. This is how I import the socket instance and emit an event:
const { OrdersService } = require('../services');
const io = require('../socket/index').get();
module.exports = {
create: async (req, res) => {
const { body } = req;
try {
const order = await OrdersService.create(body);
io.emit('new order', order);
res.status(201).json(order);
} catch (err) {
res.status(400).json(err);
}
},
};
What am I doing wrong? Any help would be greatly appreciated :)
I configured socket.io like I did based on previous questions that were raised on this topic here in stackoverflow.
In my express app, generated with express-generator, I want to use the io of socket.io in some other controller files to emit data to client sockets. My approach is below, but I get the following error with that. It would be a great favor if someone can help me in this case.
(node:11376) UnhandledPromiseRejectionWarning: TypeError: io.emit is not a function
at F:\backend\controllers\LessonController.js:169:9
In the express apps, generated by express-generator, the process of creating the server happens in the /bin/www.js. I tried importing the io instance from there and use it in some other file, but it didn't work.
bin/www.js
#!/usr/bin/env node
var app = require('../app');
var debug = require('debug')('backend:server');
var http = require('http');
var port = normalizePort(process.env.PORT || '8080');
app.set('port', port);
var server = http.createServer(app);
const io = require('socket.io')(server);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
// several other functions are omitted for brevity
module.exports = io;
LessonController.js
const Lesson = require('../models/Lesson');
const Course = require('../models/Course');
const User = require('../models/User');
const io = require('../bin/www')
var _ = require('lodash');
module.exports = {
addComment: async (lessonId, userId, content, callback) => {
const newData = {
comments: {
user: userId,
content: content,
},
};
Lesson.findOneAndUpdate({ _id: lessonId }, { $push: newData }, {new: true})
.exec()
.then(
function (data) {
if (data) {
io.emit("comment_"+lessonId,data)
callback(null, data);
} else if (err) {
callback(err, null);
}
}
)
}
};
You can try to export the socket.io instance to the global level and access that as needed.
My project was also created with express-generator, therefore, follows the same template.
In my project, I would like to count the current number of active users in home page.
Here is an example:
bin/www
#!/usr/bin/env node
const app = require('../app');
const http = require('http').Server(app);
const io = require('socket.io')(http)
http.listen(process.env.PORT);
io.on('connection', (socket) => {
const qtd = socket.client.conn.server.clientsCount;
io.emit('novaconexao', qtd);
socket.on('disconnect', () => {
io.emit('disconnecteduser', qtd - 1);
});
});
app.set('socketio', io);//here you export my socket.io to a global
console.log('Microsservice login listening at http://localhost:%s', process.env.PORT);
server/index.js
const router = require('express').Router();
router.get('/', (req, res) => {
const io = req.app.get('socketio'); //Here you use the exported socketio module
console.log(io.client.conn.server.clientsCount)
io.emit('new-user', {qtd: io.client.conn.server.clientsCount})
res.status(200).json({ msg: 'server up and running' });
})
module.exports = router;
Following this strategy, you can use socketio in any route in your application.
Here is a solution
Create a module io.js
const sio = require('socket.io');
let io = null;
module.exports = {
//Initialize the socket server
initialize: function(httpServer) {
io = sio(httpServer);
io.on('connection', function(socket) {
console.log('New client connected with id = ', socket.id);
socket.on('disconnect', function(reason) {
console.log('A client disconnected with id = ', socket.id, " reason ==> ", reason);
});
});
},
//return the io instance
getInstance: function() {
return io;
}
}
In bin/www.js
var server = http.createServer(app);
require('path_to_io_js/io').initialize(server);
In your controllers / LessonController.js
//require the io module
const socket = require('path_to_io_js/io');
module.exports = {
addComment: async (lessonId, userId, content, callback) => {
const newData = { comments: { user: userId, content: content, }, };
Lesson.findOneAndUpdate({ _id: lessonId }, { $push: newData }, { new: true })
.exec().then(function (data) {
if (data) {
//get the io instance
const io = socket.getInstance();
io.emit("comment_" + lessonId, data)
}
callback(null, data);
}).catch(err => {
callback(err);
})
}
};
Create socketInstance.js
let io = null;
// set this when you initialize the io
const setSocketInstance = (ioInstance) => {
io = ioInstance;
};
// you can call this anywhere
const getSocketInstance = () => {
return io;
};
inside socket.js where you initialize io
const setSocketInstance = require("./socketInstance");
const initializeIO = (server) => {
const io = require("socket.io")(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
},
});
// as soon as we initialize the io, we set the instance
setSocketInstance(io);
// ....
};
Now you can call getSocketInstance anywhere in your app.
I have the following structure of my project in node
project structure
in a app.js, let's say i have a app.js like this
'use strict'
const express = require('express');
const realtime = require('./controllers/realtime');
const app = express();
const server = require('http').Server(app);
var sessionMiddleware = session(sessionConfig);
realtime(server,sessionMiddleware);
in a controllers/realtime.js
"use strict";
const config = require("../config");
module.exports = (server,sessionMiddleware) => {
const io = require('socket.io')(server);
io.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
io.on('connection', (socket) => {
socket.on('statusConnetion',(data)=>{
console.log(data)
});
socket.on('disconnect', function () {
console.log(socket.id,"Un socket se desconecto");
});
console.log(`New socket connection: ${socket.id}`);
});
}
in controllers/cargos.js
const express = require('express');
const router = express.Router();
let cargos = {};
cargos.update = (req, res, next) =>{
//How can I use sockets here?
}
module.exports = cargos;
How can I use sockets in the file cargos.js and other controllers?
You can export not only the function that initiates the server, but a class that handles all the socket.io connection and functionality. This class will be a singleton and will have functions that uses the connection, and can be usable in the different modules.
example:
app.js:
'use strict'
const express = require('express');
const realtime = require('./controllers/realtime');
const app = express();
const server = require('http').Server(app);
var sessionMiddleware = session(sessionConfig);
realtime.connect(server,sessionMiddleware);
realtime.js:
let connection = null;
class Realtime {
constructor() {
this._socket = null;
}
connect(server,sessionMiddleware) {
const io = require('socket.io')(server);
io.use(function(socket, next) {
sessionMiddleware(socket.request, socket.request.res, next);
});
io.on('connection', (socket) => {
this._socket = socket;
this._socket.on('statusConnetion',(data)=>{
console.log(data)
});
this._socket.on('disconnect', function () {
console.log(socket.id,"Un socket se desconecto");
});
console.log(`New socket connection: ${socket.id}`);
});
}
sendEvent(event, data) {
this._socket.emit(event, data);
}
registerEvent(event, handler) {
this._socket.on(event, handler);
}
static init(server,sessionMiddleware) {
if(!connection) {
connection = new Realtime();
connection.connect(server,sessionMiddleware);
}
}
static getConnection() {
if(!connection) {
throw new Error("no active connection");
}
return connection;
}
}
module.exports = {
connect: Realtime.init,
connection: Realtime.getConnection
}
cargos.js:
const express = require('express');
const router = express.Router();
let cargos = {};
cargos.update = (req, res, next) =>{
const connection = require('./controllers/realtime').connection();
connection.sendEvent("update", {params: req.params});
}
module.exports = cargos;
Use separate endpoints for each controller like:
var io = require('socket.io').listen(80);
var user_controller = require('./controllers/user');
var chat_controller = require('./controllers/chat');
var user= io
.of('/user')
.on('connection', function (socket) {
user_controller.userData(user,socket);
});
var chat = io
.of('/chat')
.on('connection', function (socket) {
chat_controller.chatData(chat,socket);
});
And in controller simply export the module like this:
module.exports.chatData = function(endpoint,socket){
// this function now expects an endpoint as argument
socket.on('chat',function(newsreel){
// as is proper, protocol logic like
// this belongs in a controller:
endpoint.emit(chatreel); // broadcast chat to everyone subscribing
// to our endpoint/namespace
});
}
Same way you can use socket.io in multiple controllers by separating endpoints
I have the following code
var app = require('express')();
var http = require('http');
var server = http.createServer();
var socket = require('socket.io');
var io = socket.listen( server );
io.sockets.on('connection', function(socket) {
console.log('socket connected');
socket.broadcast.emit('newUser', 'New User Joined, Say Hi :D');
socket.on('serverEmit',function(msg) {
console.log('works');
});
socket.on('chatMessage', function(msg) {
io.emit('server_emit', msg);
console.log(msg);
});
});
server.listen(3500, function() {
console.log('listening on *:3500');
});
So my question is how to add an emit function outside of this socket connection. For example, if I have a get request like below
app.get('/link',function(req,res) {
io.sockets.emit('trigger','triggered'); // Process I want to make
});
Thanks in advance.
You need to export your io first so that it can be reusable.
socket-setup.js
const socket = require("socket.io")
let _io;
const setIO = (server) => {
_io = socket(server, {
cors : {
origin : "*",
headers : {
"Access-Control-Allow-Origin" : "*"
}
}
})
return _io
}
const getIO = () => {
return _io
}
module.exports = {
getIO,
setIO
}
Then in your app entry file (index.js), setup your io.
const app = express()
const server = http.createServer(app)
let io = setIO(server)
io.on("connection", socket => {
//Your job
})
Then wherever, e.g. message.js you want to emit event. You can use io like this.
const onMessageRecieved = () => {
try {
getIO().emit("hello", "Bye")
} catch (error) {
console.log(error);
}
}
That's it. Enjoy.
You almost had it:
it's io.emit('trigger','triggered');
And if you need to emit to a namespace you can do:
const namespace = io.of("name_of_your_namespace");
namespace.emit('trigger','triggered');