Node.js mongodb trouble with callbacks - javascript

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.

Related

How do I get this function to run synchronously?

async await is very confusing. I have the below code and here I am trying to implement a create user function that first needs to check if the user exists in my MySQL database. I have the initial setting of the user where I call await on my findByUsername function. However, The function finishes after my if(!user) code has been executed.
I want the program to wait for the findByUsername function to finish and then perform the check on whether the user exists or not.
const { username, password, firstname, permission } = req.body;
let user = await User.findByName(req.body.username, (err, data) => {
if (err) {
if (err.kind === "not_found") {
console.log("finished");
return null;
} else {
console.error("error occurred");
}
} else {
console.log("finished too");
return data;
}
});
if (!user) {
console.log("couldnt find user");
res.status(404).send("couldnt find it :(");
} else {
console.log("found user");
res.send("found them");
}
===EDIT===
I am getting another that has also been confusing me where it says that result is not a function inside of my findByName function on my User model.
sql.query(`SELECT * from users WHERE username = '${username}'`, (err, res) => {
if (err) {
console.log("error ", err);
result(err, null);
return;
}
if (res.length) {
console.log("found user: ", res[0]);
result(null, res[0]);
return;
}
console.log("couldnt find");
return { kind: "not_found" }, null;
});
};
We must use await inside an async function.
For example:
const getUser = async (username) => await User.findByName(username);
Then call that function inside a try catch
try {
user = getUser(someUsername)
} catch (error) {
// Handle errors here
}
// Code here will only run once getUser is finished
Since i don't know your UserService i don't know if "User.FindByName" is a async function. but if that is the case you could do it like this:
const { username, password, firstname, permission } = req.body;
try {
let user = await User.findByName(req.body.username);
if (!user) {
console.log("couldnt find user");
res.status(404).send("couldnt find it :(");
} else {
console.log("found user");
res.send("found them");
}
} catch (e) {
res.status(400).send(e);
}
Are you able to change the definition of User.findByName?
Instead of using await and also passing in a callback, why not remove the callback and process it synchronously?
let user = await User.findByName(username);
// Handle response here
this will solve your Problem If you are using sequelize with mysql you can use where
condition in your code so you can use more conditions in it
const { username, password, firstname, permission } = req.body;
try {
let user = await User.findOne({
where: { username: req.body.username },
attributes: { exclude: ['password', 'signup_token'] }
});
if (!user) {
console.log("couldnt find user");
res.status(401).send("couldnt find user with username" +
(req.body.username));
} else {
console.log("User Found");
return res.status(200).send("User Found With Username" +
(req.body.username));
}
} catch (error){
console.log(error);
return res.status(500).send(error);
}
The function finishes after my if(!user) code has been executed.
That is because you are mixing async/await with callbacks which you should not.
// Here you are mixing async/await and callbacks
let user = await User.findByName(req.body.username, (err, data) => { /*....*/ });
// it is the same as
function findByNameCallback(err, data) { /*.....*/ }
let user = await User.findByName(req.body.username, findByNameCallback);
// -- So you either remove the callback from the equation
var user;
try {
user = await User.findByName(req.body.username);
} catch (err) {
// Handle error
}
// Work with the user object...
// ----
// Or remove the async/await stuff
User.findByName(req.body.username, findByNameCallback);

Mongoose.js find() inside a find()

Is it possible to do a find inside a find on Node with Mongoose?
I'm letting a user change their email address, but I have to make sure the email hasn't been used by any other user before I save the new email.
I want to do this:
/*************************************************************
* MY_USER - PROFILE UPDATE
*************************************************************/
app.put('/api/myuser/info', auth, function(req, res) {
serverLog.log(req, production, {});
// User ID
var myUserID = req.session.passport.user._id;
if ( myUserID && validateID(myUserID) ) {
User.findOne({
_id: myUserID
}, function(err, data) {
if (err) throw err;
if (data == null) {
res.sendStatus(401);
console.log(401);
}
// Data
else {
// Update Email
if (req.body.email) {
// Check valid email
if ( validateEmail(req.body.email) ) {
console.log('validateEmail');
// Check Unique Email
User.findOne({
'local.user.info.email': email
}, function(err, user) {
if(err) return err;
if ( user ) {
// Email already in use
res.status(400).send('ERROR: Email Already in Use');
return;
}
console.log('uniqueEmail ' + true);
// Update email
user.local.user.info.email = req.body.email;
})
}
// Bad Email
else {
res.status(400).send('ERROR: Not a propper Email');
return;
}
}
// SAVE USER DATA
if ( info_sent ) {
user.save(function(err, data) {
if (err) throw err;
res.setHeader('Content-Type', 'application/json');
res.status(200).send(JSON.stringify(data.local.user.info, null, 3));
return;
});
}
// NO INFO WAS SENT
else {
res.status(400).send('ERROR: No information was sent.');
return;
}
}
});
}
// Bad / No User ID
else {
res.sendStatus(401);
}
});
I find the user, then check if email is in use
How would one go about doing this?
This doesn't work because you do not save your user in the callback function that checks if email already exists. Also consider using Promise to avoid callback hell
Anyway, you can do it like this:
// Check Unique Email
User.findOne({'local.user.info.email': email }, (err, user) => {
if (err) throw err;
if (user) {
return res.status(400).send('ERROR: Email Already in Use');
} else { // SAVE USER DATA
if (info_sent) {
user.save((err, data) => {
if (err) throw err;
res.setHeader('Content-Type', 'application/json');
return res.status(200).send(JSON.stringify(data.local.user.info, null, 3));
});
} else {
return res.status(400).send('ERROR: No information was sent.');
}
}

NodeJs check username and password return null

I'm trying to check the entered username and password stored in a database.
My solution is not correct and I think there might be something better than my code.
Here it is thus far:
function login (username, password, callback) {
var query = "SELECT * FROM users WHERE username = ?";
connection.query(query, [username], function (err, results) {
if (err) return callback(err);
if (results.length === 0) return callback();
var user = results[0];
if (!bcrypt.compareSync(password, user.password)) {
return callback();
}
callback(null, {
id: user.id.toString(),
});
});
}
app.get('/salam', function (req, res) {
var username = 'mahdi';
var originalPassword = 'a';
login(username , originalPassword,function (callback) {
console.log(callback);
});
});
In my code, console.log(callback); returns null, but usernames and passwords are correct. How can I fix this?
In your success callback function, you are having 2 arguments but in error callback, only one argument.
In error and success case, value of first parameter will always be null and in if (!bcrypt.compareSync(password, user.password)) { case, value of first argument will be undefined as there is no value being passed as argument.
Suggestion: Use first argument as Boolean(false or true) and based on the value, handle the callback.
function login(username, password, callback) {
var query = "SELECT * FROM users WHERE username = ?";
connection.query(query, [username], function(err, results) {
if (err) return callback(false);
if (results.length === 0) return callback();
var user = results[0];
if (!bcrypt.compareSync(password, user.password)) {
return callback(false);
}
callback(true, {
id: user.id.toString(),
});
});
}
app.get('/salam', function(req, res) {
var username = 'mahdi';
var originalPassword = 'a';
login(username, originalPassword, function(success, value) {
if (success) {
console.log(value);
}
});
});
It should be, because you didn't pass anything in callback. Change like this :
function login (username, password, callback) {
var query = "SELECT * FROM users WHERE username = ?";
connection.query(query, [username], function (err, results) {
if (err) return callback(err);
if (results.length === 0) return callback(null, false);
var user = results[0];
if (!bcrypt.compareSync(password, user.password)) {
return callback(null, false);
}
callback(null, true, {
id: user.id.toString(),
});
});
}
app.get('/check', function (req, res) {
var username = 'mahdi';
var originalPassword = 'a';
login(username , originalPassword,function (err, result, id) {
console.log(err);
console.log(result);
console.log(id);
});
});
result is for finding out true|false of action. And id means when result is true
Also err for callback is needed to error handling

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.

How to wait for a response from a mongo findOne query in a node/express app before using the response in following control flow

I am new to node, and also JavaScript callbacks.
I am trying to check if an account exists in mongo and then 'save it' if it doesn't and return an error if it does.
I am currently trying to figure this out outside of my express app. This is what i have..
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost:27017/main', function (err, db) {
if(err) throw err;
var query = { name : "www.website.com"}
findOne(db, query, function (doc) {
if(doc) {
console.log('account exists');
} else {
console.log('good to go');
}
console.dir(doc);
});
});
var findOne = function (db, query, callback) {
db.collection('accounts').findOne(query, function (err, doc) {
if(err) throw err;
db.close();
callback();
});
}
with the console.dir(doc); above returning as undefined. How do I wait for the findOne to return before using the callback to console.log or save the account?
The reason you are getting undefined is because when you call your callback your are not passing it the doc. That line should look like callback(doc).
Here is an updated version of your code with a few suggestions:
MongoClient.connect('mongodb://localhost:27017/main', function (err, db) {
if(err) throw err;
var query = { name : "www.website.com"}
findOne(db, query, function (err, doc) {
if(err) {
// something went wrong
console.log(err);
return;
}
if(doc) {
console.log('account exists');
console.dir(doc);
} else {
console.log('good to go');
}
});
});
var findOne = function (db, query, callback) {
db.collection('accounts').findOne(query, function (err, doc) {
db.close();
if(err) {
// don't use throw when in async code
// the convention is to call your callback with the error
// as the first argument (notice that I added an argument
// to the definition of your callback above)
callback(err);
}
else {
// call your callback with no error and the data
callback(null, doc);
}
});
}

Categories

Resources