How can i export socket.io into other modules in nodejs? - javascript

I have socket.io working in app.js but when i am trying to call it from other modules its not creating io.connection not sure ?
app.js
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var ditconsumer = require('./app/consumers/ditconsumer');
ditconsumer.start(io);
server.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
consumer.js
module.exports = {
start: function (io) {
consumer.on('message', function (message) {
logger.log('info', message.value);
io.on('connection', function (socket) {
socket.on('message', function(message) {
socket.emit('ditConsumer',message.value);
console.log('from console',message.value);
});
});
});
}
}

Since app.js is usually kind of the main initialization module in your app, it will typically both initialize the web server and socket.io and will load other things that are needed by the app.
As such a typical way to share io with other modules is by passing them to the other modules in that module's constructor function. That would work like this:
var server = require('http').createServer(app);
var io = require('socket.io')(server);
// load consumer.js and pass it the socket.io object
require('./consumer.js')(io);
// other app.js code follows
Then, in consumer.js:
// define constructor function that gets `io` send to it
module.exports = function(io) {
io.on('connection', function(socket) {
socket.on('message', function(message) {
logger.log('info',message.value);
socket.emit('ditConsumer',message.value);
console.log('from console',message.value);
});
});
};
Or, if you want to use a .start() method to initialize things, you can do the same thing with that (minor differences):
// app.js
var server = require('http').createServer(app);
var io = require('socket.io')(server);
// load consumer.js and pass it the socket.io object
var consumer = require('./consumer.js');
consumer.start(io);
// other app.js code follows
And the start method in consumer.js
// consumer.js
// define start method that gets `io` send to it
module.exports = {
start: function(io) {
io.on('connection', function(socket) {
socket.on('message', function(message) {
logger.log('info',message.value);
socket.emit('ditConsumer',message.value);
console.log('from console',message.value);
});
});
};
}
This is what is known as the "push" module of resource sharing. The module that is loading you pushes some shared info to you by passing it in the constructor.
There are also "pull" models where the module itself calls a method in some other module to retrieve the shared info (in this case the io object).
Often, either model can be made to work, but usually one or the other will feel more natural given how modules are being loaded and who has the desired information and how you intend for modules to be reused in other circumstances.

If you want to avoid the global scope, make your io exist in a separate file like this:
var sio = require('socket.io');
var io = null;
exports.io = function () {
return io;
};
exports.initialize = function(server) {
return io = sio(server);
};
Then in app.js:
var server = require('http').createServer(app);
var io = require('./io').initialize(server);
require('./app/consumers/ditconsumer'); // loading module will cause desired side-effect
server.listen(...);
and in consumer.js:
require('../io').io().on('connection', function(socket) {
logger.log('info', message.value);
socket.on('message', function(message) {
socket.emit('ditConsumer',message.value);
console.log('from console',message.value);
});
});

I found a simple solution.
Use a global variable in app.js and access it from the other files.
global.io = require('socket.io').listen(server);

You can make a singleton instance in just 4 lines.
In websocket.js write your server configuration code.
const socketIO = require('socket.io');
const server = require('http').createServer();
server.listen(8000);
module.exports = socketIO(server);
Then in your consumer.js just require the file
const socket = require('./websocket');
/* API logic here */
socket.emit('userRegistered', `${user.name} has registered.`);

What worked best for me was to use a callback function which exports the socket.io instance.
app.js:
var server = require('http').createServer(app);
var io = require('socket.io')(server);
io.on('connection', function(socket) {
socket.on('message', function(message) {
logger.log('info',message.value);
socket.emit('ditConsumer',message.value);
console.log('from console',message.value);
});
});
function getSocketIo(){
return io;
}
module.exports.getSocketIo=getSocketIo
and in the consumer.js
const app=require('./app.js')
const io=app.getSocketIo()

You can do this very easily you have to only write socket connection in app.js and than you can use socket anywhere you want
In app.js file put code like below
var http = require('http').createServer(app);
const io = require('socket.io')(http);
io.sockets.on("connection", function (socket) {
// Everytime a client logs in, display a connected message
console.log("Server-Client Connected!");
socket.join("_room" + socket.handshake.query.room_id);
socket.on('connected', function (data) {
});
});
const socketIoObject = io;
module.exports.ioObject = socketIoObject;
http.listen(port, () => {
console.log('Magic happens on port ' + port); // shoutout to the user
});
In any file or in controller you can import that object like below
const socket = require('../app'); //import socket from app.js
//you can emit or on the events as shown
socket.ioObject.sockets.in("_room" + req.body.id).emit("msg", "How are You ?");
This solved my problem easily

You can create a singleton class for socket-io.
It looks so clean and modular.
Here is my folder structure.
Socket.io.ts
In this file, I initializing socket-io, and created publishEvent() method so I can publish events.
import {
Server,
Socket
} from "socket.io";
import {
Events
} from "./utils";
export class SocketInit {
private static _instance: SocketInit;
socketIo: Server;
constructor(io: Server) {
this.socketIo = io;
this.socketIo.on("connection", (socket: Socket) => {
console.log("User connected");
});
SocketInit._instance = this;
}
public static getInstance(): SocketInit {
return SocketInit._instance;
}
public publishEvent(event: Events, data: any) {
this.socketIo.emit(event, data);
}
}
Server.ts
import "reflect-metadata";
import {
config
} from "dotenv";
config();
import http from "http";
import express, {
Request,
Response
} from "express";
import {
Server,
Socket
} from "socket.io";
import mongoose from "mongoose";
import cors from "cors";
import path from "path";
import morgan from "morgan";
import {
SocketInit
} from "./socket.io";
import {
downloadsRouter
} from "./routes/downloads";
const app = express();
const server = http.createServer(app);
export const io = new Server(server, {
cors: {
origin: "*"
},
});
//Initilize socket
new SocketInit(io);
mongoose
.connect("mongodb://localhost:27017/youtube", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to database");
})
.catch((error) => {
throw error;
});
app.use(morgan("dev"));
app.use(express.json());
app.use(express.urlencoded({
extended: true
}));
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, "views")));
app.use(cors());
app.use(downloadsRouter);
app.get("/", (req: Request, res: Response) => {
res.render("index");
});
server.listen(3000, () => {
console.log("Server running up 3000");
});
download-queue.ts In this file where I performing some download tasks and emit events for clients.
import Bull from "bull";
import ytdl from "ytdl-core";
import fs from "fs";
import {
Video
} from "../models/video";
import {
Events
} from "../utils";
import {
SocketInit
} from "../socket.io";
const downloadQueue = new Bull("download queue", {
redis: {
host: process.env.REDIS_HOST!,
port: parseInt(process.env.REDIS_PORT!),
},
});
);
downloadQueue.process((job, done) => {
return new Promise((resolve, reject) => {
const title = Math.random().toString();
const {
youtubeUrl
} = job.data;
//Get singleton instance
const socket = SocketInit.getInstance();
ytdl(youtubeUrl)
.pipe(fs.createWriteStream(`${process.cwd()}/downloads/${title}.mp4`))
.on("finish", async() => {
socket.publishEvent(Events.VIDEO_DOWNLOADED, title);
console.log("Download complete");
const file = `${process.cwd()}/downloads/${title}.mp4`;
const video = new Video({
title,
file,
});
await video.save();
done();
resolve({
title
});
})
.on("ready", () => {
console.log("Download started");
socket.publishEvent(Events.VIDEO_STARTED, title);
})
.on("error", (error) => {
socket.publishEvent(Events.VIDEO_ERROR, error);
done(error);
reject(error);
});
});
});
export {
downloadQueue
};

I created a file socket.service.ts with a class SocketService and in app.tsI called the constructor with the http. This will also work with pure javascript, just change the imports and exports ...
import * as socketIo from 'socket.io';
export class SocketService {
io: any;
constructor(http) {
this.io = socketIo(http)
this.io.set('origins', '*:*');
this.io.on('connection', function (socket) {
console.log('an user connected');
socket.on('disconnect', function () {
console.log('user disconnected');
});
});
http.listen(3001, function () {
console.log('socket listening on *:3001');
});
}
}
in app.ts call is like:
import * as express from 'express';
import { SocketService } from './services/socket.service';
const app = express();
var http = require('http').Server(app);
// ...
new SocketService(http);
// ...
module.exports = app;
Please notice that everytime you call the constructor a new instance will be created. To avoid this use the singleton pattern :)

Related

How to separate all of socket.io to a different file

Right now, I have app.js where I have my usual code and my socket.io code. But what I want to do is, separate every single code of socket.io into 1 different file and require that socket.io code from the different file into my main app.js. Is there any way to do it writing 2/3 lines of code into my app.js to require socket.io from a different file?
Note: I do not want to write any of my socket.io code into my app.js so I do not know if it would be possible to require('....') it into my app.js from a different file. Ideally want to separate everything within io.on('connection'){}
const express = require('express');
const http = require('http'); // socket.io is created upon http server. A way to create server
const cors = require('cors');
const {Server} = require('socket.io');
const app = express();
app.use(cors());
const server = http.createServer(app); // this is to create http server with express for socket.io
const io = new Server(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"]
}
});
io.on("connection", (socket) => {
socket.on("newUser", (username) => {
addNewUser(username, socket.id);
console.log('connect print: '); printUsers();
})
socket.on("disconnect", () => {
removeUser(socket.id);
console.log('disconnect print: '); printUsers();
});
})
server.listen(3001, () => {
console.log('server listening on port 3001');
})
There would be a few way to do this but something like below should work.
In your new file, use module.exports to export an object containing any functions or objects you want to export:
//socketio.js
module.exports = {
getIo: (server) => {
const io = new Server(server, {
//...
});
io.on("connection", (socket) => {
//...
}
//your other functions
return io;
}
}
Then in your app.js, require it and use the object's properties:
//app.js
const socketio = require('./socketio.js');
//after creating your server etc
const io = socketio.getIo(server);

Socket.io global sending Function - Node.js

I build a simple node.js / socket.io app.
I need for emitting events a module, that can be accessible through other modules so I can send socket messages when there is for example a new database entry.
Something like this:
function sendWebsocketEvent (whereToSend, EventName, Payload) {
io.in(whereToSend).emit(EventName, Payload)
}
How can I handle that?
I've tried that:
-- app.js --
// Websockets
var http = require('http').createServer(app)
var io = require('socket.io')(http)
http.listen(3000, function () {
console.log('listening on Port *:3000')
})
require('./WebSockets/socketInit')(io)
-- socketInit.js --
module.exports = (ioInput) => {
const io = ioInput
return io
}
-- sendSockets.js --
const io = require('./socketInit')
module.exports = {
sendWebsocketEvent (whereToSend, EventName, Payload) {
io.in(whereToSend).emit(EventName, Payload)
}
}
And I tried to call this function at another module:
const sendSockets = require('../WebSockets/sendSockets')
.
.
.
sendSockets.sendWebsocketEvent('user', 'databaseUpdate', 'dataToSend')
.
.
.
But this doesn't work.
Is there an other way for getting this done?
Chris
Inside Server.js
const express = require('express');
const app = express();
const server = require('http').createServer(app)
const io = socket.listen(server);
global.io = io;
const port = process.env.PORT || 5500;
server.listen(port, () => console.log(`%s 🚀 Server is listening on port ${port}`, chalk.green('✓')));
// socket io connection
let interval;
io.on("connection", socket => {
console.log("New client connected");
if (interval) {
clearInterval(interval);
}
});
And emit where you want just like that
global.io.emit('EventName', data);

io.sockets.on not working inside a route in Node.js

I'm trying to use io.sockets.on inside a route in a Node.js and Express app. I have been following what is said here: https://stackoverflow.com/a/31277123/8271839
I can successfully send io.sockets.emit events, but I cannot receive events with io.sockets.on.
Here is my code:
index.js:
const cors = require('cors');
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const taskRequest = require('./routes/taskRequest');
app.use(cors())
app.use(express.json());
app.use('/api/taskRequest', taskRequest);
app.set('socketio', io);
server.listen(4002);
io.sockets.on("connection",function(socket){
console.log("connected");
socket.on("connected", function (data) {
console.log("hello");
})
});
routes/taskRequest.js:
const express = require('express');
const router = express.Router();
router.post('/', async (req, res) => {
var io = req.app.get('socketio');
//pickedUser is one of the connected client
var pickedUser = "JZLpeA4pBECwbc5IAAAA";
//we only send the emit event to the pickedUser
io.to(pickedUser).emit('taskRequest', req.body);
io.on('connection', function (socket) {
console.log('connected 2');
socket.on('taskResponse', function () {
console.log('hello 2');
});
});
});
module.exports = router;
When a client is connected, I get the "connected" message in console, but not the "connected 2" message.
Also, when client emits "connected" message, I get "hello" in console, but when clients emits "taskResponse" message, I don't get "hello 2" in console.
Though when io.to(pickedUser).emit('taskRequest', req.body); is called, it works, client receives the "taskRequest" message.
Why is .emit() working inside my route but not .on() ?
According to you code, io is a Socket.IO server instance attached to an instance of http.Server listening for incoming events. Then inside the route you are again attaching a instance to listen to to incoming events which does not work. the io.to(pickedUser).emit works because the server instance with socketio is correctly listening to the connection thus giving the console.log("connected");.
index.js:
const cors = require('cors');
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const taskRequest = require('./routes/taskRequest');
app.use(cors())
app.use(express.json());
app.use('/api/taskRequest', taskRequest);
app.set('socketio', io);
server.listen(4002);
routes/taskRequest.js:
const express = require('express');
const router = express.Router();
router.post('/', async (req, res) => {
var io = req.app.get('socketio');
//pickedUser is one of the connected client
var pickedUser = "JZLpeA4pBECwbc5IAAAA";
io.on('connection', function (socket) {
console.log('connected 2');
io.to(pickedUser).emit('taskRequest', req.body);
socket.on('taskResponse', function () {
console.log('hello 2');
});
});
});
module.exports = router;
I mark TRomesh answer as the right answer, since indeed you can only have one io.on('connection', function (socket) {}) in your code.
Now here is what I have done to make it work for me: the issue was that if you place io.on('connection', function (socket) {}) within your router.post('/', async (req, res) => {}), it will only be triggered when you call your endpoint. In my case, I had some sockets events that I wanted to be called at anytime, not only when the endpoint is called. So I had to place the io.on('connection', function (socket) {}) outside of my router.post('/', async (req, res) => {}). Thus I couldn't use var io = req.app.get('socketio'); inside the router. Here is what I have done instead:
index.js:
const cors = require('cors');
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const taskRequest = require('./routes/taskRequest')(io);
app.use(cors())
app.use(express.json());
app.use('/api/taskRequest', taskRequest);
server.listen(4002);
routes/taskRequest.js
const express = require('express');
const router = express.Router();
module.exports = function(io) {
//we define the variables
var sendResponse = function () {};
io.sockets.on("connection",function(socket){
// Everytime a client logs in, display a connected message
console.log("Server-Client Connected!");
socket.on('connected', function(data) {
//listen to event at anytime (not only when endpoint is called)
//execute some code here
});
socket.on('taskResponse', data => {
//calling a function which is inside the router so we can send a res back
sendResponse(data);
})
});
router.post('/', async (req, res) => {
//pickedUser is one of the connected client
var pickedUser = "JZLpeA4pBECwbc5IAAAA";
io.to(pickedUser).emit('taskRequest', req.body);
sendResponse = function (data) {
return res.status(200).json({"text": "Success", "response": data.data});
}
});
return router;
};

Passing a Socket.IO instance to route files

I am looking to share the a socket.io instance with my express route files.
I previously had the socket.io listener in one routes file, orders.js on port 5200 and the express server listening in app.js on port 5000, however my cloud service required me to use one port for both, so I did have moved them both to app.js. The code below has been drastically simplified to remove noise
./app.js
const port = process.env.PORT || 8000;
const socket = require('socket.io');
const server = app.listen(port, () => console.log(`[!] Listening on
port: ${chalk.green(port)}`))
const io = module.exports = socket(server);
app.use(express.json());
app.use(cors());
app.use('/', router)
./routes/index
const express = require('express');
const router = express.Router();
router.use('/orders', require('./orders'));
module.exports = router;
./routes/orders.js
const express = require('express');
const router = express.Router();
const io = require('../index');
io.on('connection', (client) => {
console.log("CLIENT CONNECTED");
})
router.get(... etc etc
I expect to get a connection console but instead I'm getting an error that IO is not a function. When I console.log(io) I get {}
Try this way
const user = require('{SET FILE PATH}');
io.on('connection', function (socket) {
console.log('Socket connected...', socket.id);
socket.on('login', function (req) {
user.login(socket, req, 'login'); // socketObj , req , methodName
})
});
user.js
class User {
login(socket, req, methodName) {
console.log("Socket ref is ::: " , socket);
}
}
module.exports = new User();

Socket.io trigger event from controller of HTTP API

In my Node+Express server app I'd like to emit notifications from specific controllers triggered by requests coming from the HTTP API:
For example:
router.route('/status').put(Order.changeStatus)
class Order {
static changeStatus(req, res) {
// Modifying database record with changed status
// Emiting message to specific socket room
}
}
I'm handling socket connections as follows:
import AuthMiddleware from '../middleware/AuthMiddleware';
const socketio = require('socket.io');
let io;
module.exports.listen = function(app){
io = socketio.listen(app);
io.on('connection', function(socket){
console.log('Client connected');
AuthMiddleware.authenticateSocket(socket, (err, user) => {
socket.join(user._id);
console.log('Client joined to: ' + user._id);
})
socket.on('disconnect', () => {
console.log('Client disconnected')
});
});
return io;
}
After authenticating the incoming socket connection, it is subscribed to the room of its own ID.
The socket is initialised at the top of the project as follows:
import app from './app';
import http from 'http';
const PORT = process.env.PORT || 7235;
const server = http.createServer(app);
const io = require('./socket/main').listen(server);
// App start
server.listen(PORT, err => {
console.log(err || `Server listening on port ${PORT}`);
});
Although I'm able to process incoming events and respond to those, the problem with this structure is that I can't access the io object within the controllers to be able to emit data in a particular room as follows:
io.sockets.in(userID).emit('notification', data);
What would be the proper structure that enables to trigger socket events from the controllers of the HTTP API?
Your controllers need to have access to the configured io object. The easiest way to do that is to export it in your module instead of just exporting the listen function, perhaps like so:
import AuthMiddleware from '../middleware/AuthMiddleware';
const socketio = require('socket.io');
let io;
function listen(app){
io = socketio.listen(app);
io.on('connection', function(socket){
console.log('Client connected');
AuthMiddleware.authenticateSocket(socket, (err, user) => {
socket.join(user._id);
console.log('Client joined to: ' + user._id);
})
socket.on('disconnect', () => {
console.log('Client disconnected')
});
});
return io;
}
module.exports = {
listen, io
}
Then your controllers can just do const io = require('./path/to/that').io;

Categories