Force route in express - javascript

Its possible to force a route ?
Example:
I have this route A:
notiSchema = notification model
router.get('/set', function(req, res){
User.findById("userId". function(err, foundUser){
foundUser.notiSchemaSent.forEach(function(notiSchema, i){
if(req.user.notifications.length === 0){
req.user.notifications.unshift(notiSchema);
req.user.save();
} else {
req.user.notifications.forEach(function(userSchema, i){
if(req.user.notifications.indexOf(notiSchema) === -1){
req.user.notifications.unshift(notiSchema);
req.user.save();
}
});
}
});
});
res.json(req.user.notifications);
});
Problem here is that the 'res.json' line is read before the userB is updated
So i created this other route B:
router.get('/get', middleware.isLoggedIn, function(req, res){
res.json(req.user.notifications);
});
My Ajax:
$.get('/set', function(data){
// I only add a "fa-spin" class here
}).then(function(){
$.get('/get', function(data){
$(data).each(function(i, item){
$('.notDrop').prepend(item);
});
// Remove the "fa-spin" class
});
});
But sometimes route "B" is called before "A" ends;
So i want to know if its possible to call the "B" route only after the "A" one gets totally finished.

I rewrote your route to accumulate all the changes into req.user.notifications and then just save once at the end (if the array was modified). This allows you to then have only one .save() operation and to know when it's done by passing a callback to it.
Summary of changes:
Accumulate results in the array and only save at the end.
Only save if the array was modified.
Get rid of the special case for .length === 0 as that is not needed.
Use a callback on req.user.save() to know when it's done so we can then. send the response after the save is done.
Add error handling for .save().
Add error handling for .findById()
Here's the code:
router.get('/set', function(req, res){
User.findById("userId", function(err, foundUser){
if (err) {
console.log(err);
res.status(500).send("Error finding user.")
return;
}
let origLength = req.user.notifications.length;
foundUser.notiSchemaSent.forEach(function(notiSchema, i){
req.user.notifications.forEach(function(userSchema, i){
if(req.user.notifications.indexOf(notiSchema) === -1){
req.user.notifications.unshift(notiSchema);
}
});
});
if (req.user.notifications.length !== origLength) {
req.user.save(function(err) {
if (err) {
console.log(err);
res.status(500).send("Error saving user notifications.")
} else {
res.json(req.user.notifications);
}
});
} else {
res.json(req.user.notifications);
}
});
});
If you change your db code so you get an array of users from the find operation, then you can process those like this:
router.get('/set', function(req, res){
User.find({_id: {$in: arrayOfIds}}, function(err, foundUsers){
if (err) {
console.log(err);
res.status(500).send("Error finding user.")
return;
}
let origLength = req.user.notifications.length;
foundUsers.forEach(function(foundUser) {
foundUser.notiSchemaSent.forEach(function(notiSchema, i){
req.user.notifications.forEach(function(userSchema, i){
if(req.user.notifications.indexOf(notiSchema) === -1){
req.user.notifications.unshift(notiSchema);
}
});
});
});
if (req.user.notifications.length !== origLength) {
req.user.save(function(err) {
if (err) {
console.log(err);
res.status(500).send("Error saving user notifications.")
} else {
res.json(req.user.notifications);
}
});
} else {
res.json(req.user.notifications);
}
});
});

Related

next(err) not working in node.js

I have the issue that my next(err) is not working. It ignores error and just loads the page instead of sending HTTP status code 404.
The ldap search works fine and result looks as expected. It just doesn't return error when the else statement is hit.
The console does show the failed in log
app.use(function(req, res, next){
conn.search('dc=foo', opts, function (err, res) {
assert.ifError(err)
var entries = []
res.on('searchEntry', function (entry) {
entries.push(entry.object)
})
res.on('end', function (result) {
conn.unbind(function (err) {
console.log('Disconnecting')
if (entries.length == 1) {
next()
} else {
console.log('fail')
var err = new Error('Permission Denied')
err.status = 404
next(err)
}
})
})
})
})
I don't think next works like that. If you do not want your next middleware to be called, don't call next(error) or next() at all. Use instead :
if (entries.length == 1) {
next()
} else {
console.log('fail')
return res.status(404).send('Permission Denied')
}

Node.js 'headers already sent' when page is refreshed

I keep getting this error (after a certain amount of time) when i refresh the my 'members area' after login. I use response.redirect to redirect to the area (success) or back to sign in form (error). How can i fix this?
app.get('/sellers/login', function(request, response) {
if(request.session.sellerId){
response.redirect( '/sellers/area?logged_in=true');
}
else{
response.render('pages/sellers-login');
}
});
app.post('/authenticate', function(request, response) {
if(request.session.sellerId){
response.redirect('/area?logged_in=true');
}
else{
db.authenticate(request.body.loginid, function(err, results) {
if(err){
response.redirect('/sellers/login?err=1&logged_in=false&type=db');
}
else{
if(results.length >=1){
var hash = results[0]['hash'];
var seller_id = results[0]['id'];
bcrypt.compare(request.body.password, hash, function(err, res) {
if(res){
request.session.sellerId = seller_id;
response.redirect('/sellers/area?logged_in=true');
}
else{
response.redirect('/sellers/login?err=1&logged_in=false&type=pMatch');
}
});
}
else{
response.redirect('/sellers/login?err=1&logged_in=false&type=user');
}
}
});
}
});
app.get('/sellers/area', function(request, response) {
if(request.session.sellerId){
response.render('pages/sellers-area');
}
else{
response.redirect('/sellers/login?not_logged_in=true');
}
});
if(request.session.sellerId){
response.redirect('/area?logged_in=true');
}
db.authenticate(request.body.loginid, function(err, results) {
// ....
});
You send the header when request.session.sellerId evaluates to true by calling the response.redirect, but you continue with the the db.authenticate afterwards.
Inside of that callback you will do another redirect, even when your redirect for that response has already taken place.
Inside of the db.authenticate you have the same problem with the if(err)
EDIT
you need to use an else block (or a return) for both the if (request.session.sellerId) and the if (err) {
if (request.session.sellerId) {
response.redirect('/area?logged_in=true');
} else {
db.authenticate(request.body.loginid, function(err, results) {
if (err) {
response.redirect('/sellers/login?err=1&logged_in=false&type=db');
} else if (results.length >= 1) {
var hash = results[0]['hash'];
var seller_id = results[0]['id'];
bcrypt.compare(request.body.password, hash, function(err, res) {
if (res) {
request.session.sellerId = seller_id;
response.redirect('/sellers/area?logged_in=true');
} else {
response.redirect('/sellers/login?err=1&logged_in=false&type=pMatch');
}
});
} else {
response.redirect('/sellers/login?err=1&logged_in=false&type=user');
}
});
}
I think i've found precisely where the error originates from which lies in the db.authenticate
//db.js
exports.authenticate = function(loginid, callback) {
var sql = "select ...";
pool.getConnection(function(err, connection) {
if(err) {
console.log(err);
callback(true);
return;
}
connection.query(sql, [loginid], function(err, results) {
connection.release();
if(err) {
console.log(err);
callback(true);
return;
}
callback(false, results);
});
connection.on('error', function(err) {
console.log(err);
connection.release();
callback(true);
return;
});
});
};
The connection.on('error',... gets called, so the callback(true) activates causing this error.

Node.js mongodb trouble with callbacks

So I'm trying to create a sign up route that checks to see if the user exists first and i have the database call in a separate function that needs to return true or false when it's done. The problem is i'm not very familiar with callbacks and the whole asynchronous thing everything that i have searched for does not seem to work keeps giving me.
TypeError: callback is not a function
This is my code any help or direction would be appreciated.
function pullUserFromDatabase(username, callback) {
console.log(username); //for debug
mongodb.connect(url, function(err, db) {
if(err) {
console.log("didn't get far" + err) //for debug
}
var collection = db.collection(username);
collection.findOne({username}, function(err, item) {
if(err) {
console.log("nope it broke" + err) //for debug
} else {
console.log("it worked" + JSON.stringify(item)) //for debug
callback(true);
}
});
});
}
app.post("/signup", function(req, res) {
var username = req.headers["username"],
password = req.headers["password"],
randomSalt = crypto.randomBytes(32).toString("hex"),
passwordHashOutput = crypto.createHash('sha256').update(password + randomSalt).digest("hex");
if(!username || !password) {
res.send("Username or password not provided.")
} else if(pullUserFromDatabase(username)) {
res.send("User exist.")
}
});
You need to use the callback as follows:
function pullUserFromDatabase(data, callback) {
console.log(data.username); //for debug
mongodb.connect(url, function(err, db) {
if(err) {
console.log("didn't get far" + err) //for debug
}
var collection = db.collection(data.collection);
collection.find({"username": data.username}).count(function (err, count) {
callback(err, !! count);
});
});
};
app.post("/signup", function(req, res) {
var username = req.headers["username"],
password = req.headers["password"],
randomSalt = crypto.randomBytes(32).toString("hex"),
passwordHashOutput = crypto.createHash('sha256').update(password + randomSalt).digest("hex");
if(!username || !password) {
res.send("Username or password not provided.")
}
var data = {
username: username,
collection: "collectionName"
}
if(!username || !password) {
res.send("Username or password not provided.")
}
pullUserFromDatabase(data, function(err, exists) {
if (err) {
res.send(400, "Error - " + err);
}
else if(exists) {
res.send(200, "User exists.");
}
res.send(200, "User does not exist.");
});
});
The reason that callback is undefined is because you didn't pass a 2nd argument to pullUserFromDatabase(username) Provide a 2nd argument, eg. pullUserFromDatabase(username, function(result) {/* do something here with the result variable */})
If you're not very familiar with aync & callbacks, you might find it more intuitive to use promises, but that comes with its own learning curve.
In the context of the original code, this looks like:
...
if(!username || !password) {
res.send("Username or password not provided.");
return;
}
pullUserFromDatabase(username, function(result) {
if(result) {
res.send("User exist.");
} else {
// TODO: Handle this case. If res.send() is never called, the HTTP request won't complete
}
});
...
Also, you need to ensure your callback is always invoked. Add callback(false):
console.log("nope it broke" + err); //for debug
callback(false);
Do a similar step after "didn't get far" and then return so the callback doesn't get invoked multiple times.

async.each cannot set headers after already set

Here's what happening. I'm saving new companies first, then attaching the _id to each new user before they get saved. The issue I'm running into is returning a response. When I put the res.json() into the function thats getting repeated obviously I'm getting an error because I already have a response sent from the first time it loops through.
So, How do I call signupSeq(record, res) but wait for the async methods to finish so I know whether I have an error or not?
var signupSeq = function(req, res) {
async.waterfall([
function(callback) {
console.log(req);
if (req.company._id===undefined){
var company = new Company(req.company);
company.save(function(err){
if (err) {
console.log('save error');
callback(err);
}else{
callback(null, company._id);
}
})
}else{
callback(null, req.company._id); //pass teh plain ID if it's not a new name:xxx
}
},
function(companyId, callback) {
delete req.company
req.company = companyId
// Init Variables
var user = new User(req);
var message = null;
// Add missing user fields
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function(err) {
if (err) {
callback(err);
} else {
callback(null, user);
}
});
}
], function (err, result) {
if(err){
console.log(result+'funciton result')
return err
// res.status(400).send({
// message: errorHandler.getErrorMessage(err)
// });
}else{
console.log(result+'funciton result')
return result
//res.json(result)
}
});
}
exports.saveMany = function(req, res){
async.each(req.body, function(record, callback) {
// Perform operation on record.body here.
console.log('Processing record.body ' + record);
// Do work to process record.body here
var x = signupSeq(record, res)
console.log(x+'<<<<<<<value of x');
console.log('record.body processed');
callback();
}, function(err){
// if any of the record.body processing produced an error, err would equal that error
if( err ) {
res.json(err);
// One of the iterations produced an error.
// All processing will now stop.
console.log('A record.body failed to process');
} else {
res.json('Success');
console.log('All files have been processed successfully');
}
});
}
You could add a callback (cb) in your signupSeg function.
var signupSeq = function(req, res, cb) {
async.waterfall([
function(callback) {
console.log(req);
if (req.company._id===undefined){
var company = new Company(req.company);
company.save(function(err){
if (err) {
console.log('save error');
callback(err);
}else{
callback(null, company._id);
}
})
}else{
callback(null, req.company._id); //pass teh plain ID if it's not a new name:xxx
}
},
function(companyId, callback) {
delete req.company
req.company = companyId
// Init Variables
var user = new User(req);
var message = null;
// Add missing user fields
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function(err) {
if (err) {
callback(err);
} else {
callback(null, user);
}
});
}
], function (err, result) {
if(err){
console.log(result+'funciton result')
cb(err)
// res.status(400).send({
// message: errorHandler.getErrorMessage(err)
// });
}else{
console.log(result+'funciton result')
cb(null,result)
//res.json(result)
}
});
}
exports.saveMany = function(req, res){
async.each(req.body, function(record, callback) {
// Perform operation on record.body here.
console.log('Processing record.body ' + record);
// Do work to process record.body here
signupSeq(record, res,function(err,result){
var x= result;
console.log(x+'<<<<<<<value of x');
console.log('record.body processed');
callback();
})
}, function(err){
// if any of the record.body processing produced an error, err would equal that error
if( err ) {
res.json(err);
// One of the iterations produced an error.
// All processing will now stop.
console.log('A record.body failed to process');
} else {
res.json('Success');
console.log('All files have been processed successfully');
}
});
}
This way inside the asyn.each the signipSeg will have to finish before the call of the callback().
Hope this helps.

NodeJS passing an extra param to a callback

I'm a begginer in Node.JS and as a first tryout i'm implementing a small url shortening service that will get a request with an id parameter and redirect to the actual url after searching a cassandra database.
Below you can find my implementation.
var reqResponse;
app.get('/r/:id', function(req, res) {
reqResponse = res;
conn.connect(function(err, keyspace) {
if(err){
throw(err);
}
conn.cql(cqlSelectStatement, [req.params.id], { gzip:true }, redirectCallback);
});
});
function redirectCallback (err, results) {
if (err != null) {
//log
} else {
if (results.length > 0) {
results.every(function(row){
reqResponse.writeHead(config.redirectStatus, {
'Location': row[0].value
});
reqResponse.end();
return false;
});
} else {
reqResponse.send("There was a problem!");
return false;
}
}
conn.close();
return true;
}
It works fine, it does the job, but i'm having some doubts about that reqResponse "global" variable. I don't like it there.
Is there a way I could send "res" as a parameter to the redirectCallback function?
Thank you!
Yes there is: Create an anonymous function and use that to pass the parameter:
app.get('/r/:id', function(req, res) {
conn.connect(function(err, keyspace) {
if(err){
throw(err);
}
conn.cql(cqlSelectStatement, [req.params.id], { gzip:true }, function (err, results) { redirectCallback(err, res, results); } );
});
});
And your callback becomes:
function redirectCallback (err, res, results) {

Categories

Resources