Node + Q with expressjs - ordered promisses - node.js

I want to execute a set of functions in the order they were written and in the end to release the request to the client.
for example see the mock code bellow:
router.get('/dashboard', function(req, res, next) {
var json = {items : 0}
Q.fcall(
function(){
//first exec
json.items+=1;
}
).then(
function(){
//scond exec
json.items+=1;
}
).then(
function(){
//third exec
json.items+=1;
}
).finally(
function(){
//do this when all the other promises are don
res.json(json);
});
}
the finally function shoud be executed when all is done.
Can it be done with Q?
UPDATE
I think I mislead you, and did not give all the information, because i did not think its relevant, but it is...
I actually bringing data via mongoose, and mongoose is async asd well.
So it goes like this:
Q.fcall(
function() {
Visitor.count(dateRange, function(err, data) {
json.newVisitors = data;
});
}).then(
function() {
Account.count(dateRange, function(err, data) {
json.newAccounts = data;
});
}).finally(
function() {
res.json(json);
})

Mongoose is already promisified. Calling exec() on a query gives you a promise. Here are two ways of doing it:
Classic promises chaining:
Visitor.count(dateRange).exec().then(function (data) {
json.newVisitors = data;
return Account.count(dateRange).exec(); // return promise for chaining
}).then(function (data) {
json.newAccounts = data;
}).then(function () {
res.json(json);
}).catch(function (err) {
// handle errors
});
Or Promise.all:
Promise.all([
Visitor.count(dateRange).exec(),
Account.count(dateRange).exec()
]).then(function(result){
// result is an ordered array of all the promises result
json.newVisitors = result[0];
json.newAccounts = result[1];
}).catch(function (err) {
// handle errors
});

Yes:
var path = require('path'),
express = require('express'),
app = express(),
router = express.Router(),
Q = require('q');
router.get('/dashboard', function(req, res) {
var json = {items:''};
Q.fcall(function() {
json.items += 'A';
})
.then(function() {
json.items += 'B';
})
.then(function() {
json.items += 'C';
})
.finally(function() {
res.json(json);
});
});
app.use('/', router);
var http = require('http');
var port = process.env.PORT || '3000';
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('listening', function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log('Listening on ' + bind);
}
);
Then
curl localhost:3000/dashboard
Returns:
{"items":"ABC"}
P.S. You might also want to investigate async-q et. al.:
async.series([
->
### do some stuff ###
Q 'one'
->
### do some more stuff ... ###
Q 'two'
]).then (results) ->
### results is now equal to ['one', 'two'] ###
doStuff()
.done()
### an example using an object instead of an array ###
async.series({
one: -> Q.delay(200).thenResolve(1)
two: -> Q.delay(100).thenResolve(2)
}).then (results) ->
### results is now equal to: {one: 1, two: 2} ###
doStuff()
.done()
UPDATED (a bit forced, I would just use async):
var path = require('path'),
express = require('express'),
app = express(),
logger = require('morgan'),
router = express.Router(),
Q = require('q'),
async = require('async-q');
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
router.get('/dashboard', function(req, res) {
var json = {};
async.series({
newVisitors: function() {
return Q.Promise(function(resolve,reject) {
console.log(arguments);
Visitor.count(dateRange, function(err, data) {
if(err) return reject(err);
resolve(data);
});
});
},
newAccounts: function() {
return Q.Promise(function(resolve,reject) {
Account.count(dateRange, function(err, data) {
if(err) return reject(err);
resolve(data);
});
});
}
})
.then(function(json) {
res.json(json);
});
});
app.use('/', router);
var http = require('http');
var port = process.env.PORT || '3000';
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('listening', function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log('Listening on ' + bind);
}
);
Now returns:
{"newVisitors": 1,"newAccounts":2}

Related

After refresh "Cannot set headers after they are sent to the client"

I am using express js, mongoose and ejs as templates.
When I refresh my page, in second or third try, I get this error.
Here is my app.js :
{consts..}
var index = require('./routes/index.js');
var users = require('./routes/users.js');
app.set("port", ("2401"));
app.set("views", __dirname + "/views");
app.set("view engine", "ejs");
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/', index);
app.use('/users', users);
var port = '2401';
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
console.log('server started on port : ' + app.get('port'));
}
users router :
{consts..}
router.get('/login', function (req, res) {
users.getLogin(req, res);
});
router.post('/login', [
check('email')
.isEmail().withMessage('Please enter a valid email address')
.trim()
.normalizeEmail(),
check('terms', 'Please accept our terms and conditions').equals('yes'),
],
function (req, res) {
users.postLogin(req, res);
});
module.exports = router;
and controller :
usersController.getLogin = function (req, res) {
const connector = mongoose.connect(conStr, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.connection.on('open', function (ref) {
categories.find(function (err, cats) {
if (err) {
console.log("Error:", err);
}
else {
return res.render("../views/login.ejs", {
_: _,
categories: cats
});
}
});
});
}
index router :
var express = require('express');
var router = express.Router();
var index = require("../controllers/indexController.js");
router.get('/', function (req, res) {
index.list(req, res);
});
module.exports = router;
index controller:
var mongoose = require("mongoose");
var categories = require("../models/categories.js");
var _ = require("underscore");
var indexController = {};
indexController.list = function (req, res) {
const connector = mongoose.connect(conStr, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.connection.on('open', function (ref) {
categories.find(function (err, cats) {
if (err) {
console.log("Error:", err);
}
else {
console.log("indexe geldi");
return res.render("../views/index.ejs", {
_: _,
categories: cats,
sess: req.session
});
}
})
})
};
module.exports = indexController;
I tried almost everything but still no progress. Can it be about usage of "next()" ?
I also tried the hole router get / post functions with next, mongoose find functions with exec().
The issue is that you're opening a connection to mongo in every route and:
mongoose.connection.on('open' is firing for past requests, requests that are already finished, that's why you're getting Cannot set headers after they are sent to the client.
Move the mongoose.connect & the listener outside of each route.
usersController.getLogin = function (req, res) {
categories.find(function (err, cats) {
if (err) {
console.log("Error:", err);
return res.status(500).send('error');
}
else {
return res.render("../views/login.ejs", {
_: _,
categories: cats
});
}
});
}

TypeError: Cannot read property 'then' of undefined node js

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)
});
});
});

Extract function outside of main app.js

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;

Can't make works socket.io with my handshake

I don't understand where is my problem.
When I put this:
io = require('socket.io').listen(server).of("/");
I my handshake looks like running fine, but all my socket.io's event (like connection) never works.
And when I when i put this
io = require('socket.io').listen(server);
I got this :
TypeError: Object # has no method 'authorization'
If I remove my handshake, all the socket.io's will works, but i need it :/
Here my app.js
const express = require('express')
, app = express()
, http = require('http')
, ejs = require('ejs')
, server = http.createServer(app)
, port = process.env.PORT || 1337
, io = require('socket.io').listen(server).of("/")
;
module.exports = { app: app, server: server };
// Grab "cookie" and "connect" from express
var connect = require('express/node_modules/connect')
var cookie = require('express/node_modules/cookie')
var connectes =[];
app.configure(function(){
this.use("/public", express.static(__dirname + '/public'));
this.use(express.cookieParser());
this.use(express.json());
this.use(express.urlencoded());
this.sessionStore = new express.session.MemoryStore({ reapInterval: 60000 * 10 });
this.use(express.session({
"secret": "some private string",
"store": this.sessionStore
}));
});
app.engine('html', ejs.renderFile);
app.set('views', __dirname + '/views');
app.get('/', [requireLogin], function(request, response) {
var pseudo = request.session.pseudo;
response.render('index.ejs.html',{connectes:connectes});
});
app.get('/login', function(request, response) {
response.render('login.ejs.html');
});
app.post("/login", function(req, res){
if (req.param("user") != "") {
req.session.pseudo = req.param("user");
//socket.set('pseudo',req.param("user"));
connectes.push(req.param("user"));
res.render('index.ejs.html',{connectes:connectes});
}else{
res.render('login.ejs.html');
}
});
const parseCookie = require('connect').utils.parseCookie;
io.authorization(function (handshakeData, callback) {
var cookies = cookie.parse(handshakeData.headers.cookie);
console.log(cookie);
});
io.on('connection', function(socket){
console.log("This never appear in log :/");
socket.on('login',function(user,callback){
var pseudoValide = true;
for (var i = 0; i < connectes.length; i++) {
if(connectes[i]==user.pseudo){
pseudoValide = false;
callback(true);
}
}
if (pseudoValide) {
socket.set('pseudo',user.pseudo);
pseudo = user.pseudo;
socket.broadcast.emit('newuser', pseudo);
socket.broadcast.emit('nvluseronline', pseudo);
connectes.push(pseudo);
callback(false,pseudo);
}
});
socket.on('message',function(message){
messageUser = message.message;
socket.get('pseudo',function(error, pseudo){
io.sockets.emit('nouveauMessage',{pseudo:pseudo,message:messageUser});
})
});
socket.on('disconnect',function(){
socket.get('pseudo',function(error, pseudo){
connectes = unset(connectes, pseudo);
io.sockets.emit('nvldisc', pseudo);
})
});
});
function unset(array, value){
var output=[];
var index = array.indexOf(value)
{
var j = 0;
for(var i in array)
{
if (i!=index)
{
output[j]=array[i];
j++;
}
}
return output;
}
}
/** Middleware */
function requireLogin (req, res, next) {
if (req.session.pseudo) {
console.log(req.session.pseudo);
next();
}else{
res.redirect("/login");
}
}
if (!module.parent) {
server.listen(port, function () {
console.log('Listening', this.address());
})
}
Like you can see i'm a beginner lost !
Thank you guys !
You should read the documentation on authorization carefully. authorization method only exists for namespaces. When doing global authorization, you have to do:
io.configure(function (){
io.set('authorization', function (handshakeData, callback) {
callback(null, true); // error first callback style
});
});
It'll work fine if you have .of("/") as server endpoint, but it is a namespace. Without namespaces you would have to use above method.

How to make a insert statement in express

I have express server code below
I want to initiate an insert statement by getting the param value
Then inserting that param value into Database
What i have done so far is that i have learnt how to make JSON response ::
var express = require('express')
, async = require('async')
, http = require('http')
, mysql = require('mysql');
var app = express();
var connection = mysql.createConnection({
host: 'localhost',
user: '*****',
password: "*****",
database: 'DB-NAME'
});
connection.connect();
// all environments
app.set('port', process.env.PORT || 1234);
app.use(express.static(__dirname + '/public/images'));
app.get('/Name/',function(request,response,next){
var keyName=request.query.Key;
var name_of_restaurants;
async.series( [
// Get the first table contents
function ( callback ) {
connection.query('SELECT * FROM RestaurantDescription where RestaurantName = ?', [keyName], function (err, rows, fields)
{
console.log('Connection result error ' + err);
name_of_restaurants = rows;
callback();
});
}
// Send the response
], function ( error, results ) {
response.json({
'restaurants' : name_of_restaurants
});
} );
} );
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
What i am trying to do::
I am trying to find how to make a insert statement
So that i could extract the Param value and insert that data to
database
How to modify the above code to achieve my goal
Hope i am clear !
[EDIT]
var express = require('express')
, async = require('async')
, http = require('http')
, mysql = require('mysql');
var app = express();
var connection = mysql.createConnection({
host: 'localhost',
user: '*********',
password: "*********",
database: 'DB_NAME'
});
connection.connect();
// all environments
app.set('port', process.env.PORT || 7000);
app.use(express.static(__dirname + '/public/images'));
app.get('/Name/',function(request,response,next){
var keyName=request.query.Key;
var name_of_restaurants;
async.series( [
function(callback) {
connection.query('INSERT INTO RestaurantDescription (RestaurantName) VALUES (?)', [keyName], function (err, rows, fields)
{
console.log('Connection result error ' + err);
callback();
});
}
// Send the response
] );
} );
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
I'm not familiar with the libraries which you use but I think that you should add another function inside the array passed to async.series. The body of the function should have similar content as the one above.
async.series( [
// Get the first table contents
function(callback) {
connection.query('SELECT * FROM RestaurantDescription where RestaurantName = ?',
[keyName],
function (err, rows, fields) {
console.log('Connection result error ' + err);
name_of_restaurants = rows;
callback();
}
);
},
// inserting a value
function(callback) {
connection.query('INSERT INTO RestaurantDescription (RestaurantName) VALUES (?)',
[keyName],
function (err, rows, fields) {
console.log('Connection result error ' + err);
callback();
}
);
}
]
What will happen is that the both function will be executed asynchronous and at the end you will still send a response to the browser.
You are already getting a GET parameter via var keyName=request.query.Key;.
If you plan to use POST parameters, instead of
var keyName=request.query.Key;
You should add a middleware which parses the variables:
app.use(express.bodyParser());
And then
var keyName = request.body.Key;
I'm referring this comment https://stackoverflow.com/a/18167056/642670

Resources