I'm fairly new to nodejs, and in wanting to keep the code neat and clean, I tried to extract a function to a different file, and then require it from my main app.
I'm facing a problem that this function includes socket-io data streaming and it uses the http module that uses my express app
This is the main app, and I want to move the content of getDetails to a seperate file:
const express = require('express');
const app = express();
const spawn = require('child_process').spawn;
const execFile = require('child_process').execFile;
const server = require('http').Server(app);
const io = require('socket.io')(server);
// Set router
const router = express.Router();
// fix body of requests
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
router.get('/getDetails', (req, res) => {
const qry = req.query;
if (qry.func === 'tail') {
const tail = spawn('ssh', ['root#' + qry.srv, qry.script, qry.func, qry.serv]);
io.on('connection', function (socket) {
tail.stdout.on('data', function (data) {
socket.emit('newLine', {line: data.toString('utf8').replace(/\n/g, '<br>')});
});
tail.on('close', (code) => {
console.log('child process exited with code', code);
});
tail.stderr.on('data', (data) => {
console.log('There are some errors:', data.toString('utf8'));
socket.emit('newLine', {line: data.toString('utf8')});
});
});
res.sendStatus(200);
}
else {
execFile('ssh', ['root#' + qry.srv, qry.script, qry.func, qry.serv], {timeout: 5000}, (error, stdout, stderr) => {
if (error) {
console.error('stderr', error);
return res.status(500).send({stderr: stderr, error: error});
}
return res.status(200).send({stdout: stdout.toString('utf8')});
});
}
});
app.use('/', router);
server.listen(port, function () {
console.log('The magic happens on localhost:' + port);
});
Now I can module.exports everything on my seperate file but do I need to also require express, and http again?
And should I move the server.listen to the seperate file?
server.js
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
const route = require('./route');
// fix body of requests
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json())
app.set('socketio', io);
app.use('/',route);
server.listen(port, function () {
console.log('The magic happens on localhost:' + port);
});
route.js
const express = require('express');
const router = express.Router();
const Controller = require('./controller');
router.post('/register',Controller.getDetails);
module.exports=router;
controller.js
const spawn = require('child_process').spawn;
const execFile = require('child_process').execFile;
const controller = {
getDetails : (req,res)=>{
// now use socket.io in your controller file
var io = req.app.get('socketio');
const qry = req.query;
if (qry.func === 'tail') {
const tail = spawn('ssh', ['root#' + qry.srv, qry.script, qry.func, qry.serv]);
io.on('connection', function (socket) {
tail.stdout.on('data', function (data) {
socket.emit('newLine', {line: data.toString('utf8').replace(/\n/g, '<br>')});
});
tail.on('close', (code) => {
console.log('child process exited with code', code);
});
tail.stderr.on('data', (data) => {
console.log('There are some errors:', data.toString('utf8'));
socket.emit('newLine', {line: data.toString('utf8')});
});
});
res.sendStatus(200);
}
else {
execFile('ssh', ['root#' + qry.srv, qry.script, qry.func, qry.serv], {timeout: 5000}, (error, stdout, stderr) => {
if (error) {
console.error('stderr', error);
return res.status(500).send({stderr: stderr, error: error});
}
return res.status(200).send({stdout: stdout.toString('utf8')});
});
}
}
}
module.exports=controller;
Related
Hello I am trying to build Chat Application, but my issue with socket.io, first
<script src="socket.io/socket.io.js"></script>
is not found, if i am using
<script src="https://cdn.socket.io/4.5.4/socket.io.min.js" integrity="sha384-/KNQL8Nu5gCHLqwqfQjA689Hhoqgi2S84SNUxC3roTe4EhJ9AfLkp8QiQcU8AMzI" crossorigin="anonymous"></script>
then its working fine for me only one way conection, but
http://127.0.0.1:3000/socket.io/?EIO=4&transport=polling&t=OJDCcA5
this is not found.
Server.js
var express = require('express');
var cors = require('cors');
var mongoose = require('mongoose');
var app = express();
var http = require('http').Server(app);
app.use(cors());
var server = app.listen(3000, () => {
console.log("server is running on port", server.address().port);
});
var io = require('socket.io')(http);
//Connection Mongo Database with Mongooes
var dbUrl = 'mongodb://127.0.0.1:27017/ChatApplication';
mongoose.connect(dbUrl, {
useNewUrlParser: true,
useUnifiedTopology: true
}, (err) => {
if(err){
console.log('Mongodb Not Connected',err);
}
});
var connection = mongoose.connection;
console.log(mongoose.connection.readyState); //logs 0
mongoose.connection.on('connecting', () => {
console.log('connecting')
console.log(mongoose.connection.readyState); //logs 2
});
mongoose.connection.on('connected', () => {
console.log('connected');
console.log(mongoose.connection.readyState); //logs 1
});
mongoose.connection.on('disconnecting', () => {
console.log('disconnecting');
console.log(mongoose.connection.readyState); // logs 3
});
mongoose.connection.on('disconnected', () => {
console.log('disconnected');
console.log(mongoose.connection.readyState); //logs 0
});
connection.once("open", function() {
console.log("MongoDB database connection established successfully");
});
var bodyParser = require('body-parser')
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}))
var Message = mongoose.model('message',{ name : String, message : String})
console.log(Message);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.get('/messages', (req, res) => {
console.log("Message Recived");
Message.find({},(err, messages)=> {
res.send(messages);
})
})
app.post('/messages', (req, res) => {
var message = new Message(req.body);
//console.log(message);
message.save((err) =>{
if(err)
sendStatus(500);
io.emit('message', req.body);
res.sendStatus(200);
})
})
io.on('connection', () =>{
console.log('a user is connected')
})
//https://masteringjs.io/mongoose
//https://www.freecodecamp.org/news/simple-chat-application-in-node-js-using-express-mongoose-and-socket-io-ee62d94f5804/
Please let me know, where do i am wrong.
Trying to Real time message get from server end using socket.
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")
}
My socket event doesn't trigger when I emit it in Nodejs, I know it because I don't see "Pizza" in my console.
This is my code:
const express = require("express");
const cors = require("cors");
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server, {
});
app.use((req, res, next) => {
req.io = io;
next()
})
app.use(cors());
io.on('connection', (socket) => {
socket.on("dummy_event", (data) => {
console.log(`Socket 🍕`); // I don't see this message in my console
if(!data.name) return;
})
});
app.get("/socket_test", (req, res) => {
req.io.emit("dummy_event", {name: "pizza"})
return res.json({data: 'dummy text'})
})
server.listen(4000, () => {
console.log(`Server Started on Port 4000`);
})
I'm a new learner express.js I want to test simple post and get operations with tdd mechanism. I created the test, route, index and db files but when I try to test POST method it gives me this error.
This is my routes/task.js
const express = require('express');
const router = express.Router();
router.post("/api/task", async (req,res) => {
try {
const task = await new Task(req.body).save();
res.send(task);
} catch (error) {
res.send(error);
}
})
This is my test/task.js
let chai = require("chai");
const chaiHttp = require("chai-http");
const { send } = require("process");
let server = require("../index");
//Assertion Style
chai.should();
chai.use(chaiHttp);
describe('Tasks API', () => {
/**
* Test the POST Route
*/
describe('POST /api/task', () => {
it("It should POST a new task", () => {
const task = {task: "Wake Up"};
chai.request(server)
.post("/api/task")
.send(task)
.end((err, response) => {
response.should.have.status(201);
response.body.should.be.a('string');
response.body.should.have.property('id');
response.body.should.have.property('task');
response.body.should.have.property('task').eq("Wake Up");
response.body.length.should.be.eq(1);
done();
});
});
});
});
This is my db.js
var sqlite3 = require('sqlite3').verbose()
const DBSOURCE = "db.sqlite"
let db = new sqlite3.Database(DBSOURCE, (err) => {
if (err) {
// Cannot open database
console.error(err.message)
throw err
}else{
console.log('Connected to the SQLite database.')
db.run(`CREATE TABLE IF NOT EXISTS todo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task text
)`,
(err) => {
if (err) {
// Table already created
console.log(err);
}
});
}
});
module.exports = db
And this is my index.js
const connection = require('./db');
const express = require('express');
const app = express();
const cors = require("cors");
const port = process.env.PORT || 8080;
app.use(express.json());
app.use(cors());
app.get('/', (req, res) => {
res.send('Hello World');
});
app.post('/api/task', (req, res) => {
res.status(201).send(req);
});
app.listen(port, () => console.log(`Listening on port ${port}...`));
module.exports = app;
The thing that I try to do is building a test case to test the post method. I think I couldn't built the correct relations the files.
Currently, just by doing a POST request to /api/task, the error will appear. That is because of these lines in index.js:
app.post('/api/task', (req, res) => {
res.status(201).send(req);
});
The req parameter is circular, hence cannot be JSON-stringified.
Solution
In routes/task.js export the router:
const express = require('express');
const router = express.Router();
router.post("/api/task", async (req,res) => {
try {
const task = await new Task(req.body).save();
res.send(task);
} catch (error) {
res.send(error);
}
})
// By adding this line you can export the router
module.exports = router
In index.js, include the routes/task.js file and pass it to app.use(...), also remove the now-obsolete /api/task route:
const connection = require('./db');
const express = require('express');
const app = express();
const cors = require("cors");
const taskRoutes = require("./routes/task")
const port = process.env.PORT || 8080;
app.use(express.json());
app.use(cors());
app.get('/', (req, res) => {
res.send('Hello World');
});
app.use(taskRoutes)
app.listen(port, () => console.log(`Listening on port ${port}...`));
module.exports = app;
This way we got rid of the circular structure stringifying and the tests should now pass.
TypeError: Cannot read property 'then' of undefined
Can you help me fix this? Thank you.
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var mongodb = require('mongodb');
var dbConn = mongodb.MongoClient.connect('mongodb://localhost:27017',
function(err, db) {
if(err){
throw err;
}else{
console.log("connected");
}
})
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.resolve(__dirname, './')));
app.post('/post-feedback', function (req, res) {
dbConn.then(function(db) {
delete req.body._id; // for safety reasons
db.collection('feedbacks').insertOne(req.body);
});
res.send('Data received:\n' + JSON.stringify(req.body));
});
app.get('/view-feedbacks', function(req, res) {
dbConn.then(function(db) {
db.collection('feedbacks').find({}).toArray().then(function(feedbacks) {
res.status(200).json(feedbacks);
});
});
});
app.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0' );
TypeError: Cannot read property 'then' of undefined
Can you help me fix this? Thank you.
The following approach should get you started but should not use this for production (Reference: How do I manage MongoDB connections in a Node.js web application?). Read through for another production starters.
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var mongodb = require('mongodb');
var dbConn = function() {
return new Promise((resolve, reject) => {
mongodb.MongoClient.connect('mongodb://localhost:27017',
function(err, db) {
if(err){
return reject(err);
}else{
return resolve(db);
}
});
});
}
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.resolve(__dirname, './')));
app.post('/post-feedback', function (req, res) {
dbConn()
.then(function(db) {
delete req.body._id; // for safety reasons
db.collection('feedbacks').insertOne(req.body);
res.send('Data received:\n' + JSON.stringify(req.body));
})
.catch(err => {
console.log(err)
res.send('Error');
})
});
app.get('/view-feedbacks', function(req, res) {
dbConn()
.then(function(db) {
db.collection('feedbacks').find({}).toArray().then(function(feedbacks) {
res.status(200).json(feedbacks);
});
})
.catch(err => {
console.log(err);
res.status(500).json({});
});
});
app.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0' );
Production Starter:
Ideally you will have something like following say in a file db.js
let mongoClient = require('mongodb').MongoClient,
logger = require('winston');
function DATABASE() {
this.dbObj = null;
this.myCollection = null; // You will need to add more collections here
}
DATABASE.prototype.init = function (config, options) {
let self = this;
self.config = config; //can pass a config for different things like port, ip etc.
self.logger = logger;
return new Promise(function (resolve, reject) {
if (self.initialized) {
return resolve(self);
}
let connectionUri = "mongodb://localhost:27017"; //self.config.mongo.connectionUri;
mongoClient.connect(connectionUri, {native_parser: true}, function (err, db) {
if (err) {
reject(err);
}
else {
self.dbObj = db;
self.myCollection = db.collection('myCollection');
self.initialized = true;
self.logger.info("db init success");
return resolve(self);
}
});
});
};
var dbObj = null;
var getdbObj = function () {
if (!dbObj) {
dbObj = new DATABASE();
}
return dbObj;
}();
module.exports = getdbObj;
In your main app start file you will have something like:
let dbObj = require('./db.js');
dbObj.init()
.then(db => {
console.log('db initialized successfully');
//db.dbObj.collection('myCollection').find()
//or
//db.myCollection.find() because this has been already initialized in db.js
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.resolve(__dirname, './')));
app.post('/post-feedback', function (req, res) {
delete req.body._id; // for safety reasons
db.dbObj.collection('feedbacks').insertOne(req.body);
res.send('Data received:\n' + JSON.stringify(req.body));
});
app.get('/view-feedbacks', function(req, res) {
//db.collection('feedbacks')
});
app.listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0' )
})
.catch(err => console.log(err));
Try this, dbConn is not promise
app.post('/post-feedback', function (req, res) {
mongoose.connection.db.collection('feedbacks', function (err, collection) {
collection.insertOne(req.body);
res.send('Data received:\n' + JSON.stringify(req.body));
});
// OR
const Model = mongoose.model('feedbacks');
let model = new Model();
model = Object.assign(model, req.body);
model.save().then((result) => {
res.send('Data received:\n' + JSON.stringify(req.body));
});
});
Its working .
If you are getting any TypeError (UnhandledPromiseRejectionWarning: TypeError: db.collection is not a function) form mongodb. Just change the version of mongodb to -
"mongodb": "^2.2.33"
"use strict"
var express = require('express');
var mongodb = require('mongodb');
var app = express();
var MongoClient = mongodb.MongoClient;
var url = 'mongodb://localhost:27017/feedback';
// no need to call then() yet
var dbConn = MongoClient.connect(url);
app.set('port', 5000);
app.listen(app.get('port'), function() {
console.log('feedback is running on port', app.get('port'));
});
app.get('/view-feedback', function(req, res, next) {
// the connection is opened
dbConn.then(function(db) {
// var dbo = db.db("feedback");
db.collection('feedback').find({}).toArray().then(function(docs) {
// return docs;
res.json(docs)
});
});
});