Node function finishing before Mongoose db find method - node.js

Why could the example below be returning the node middle-ware function before my mongoose db lookup function finishes? I am sure this is a async issue but I am a bit lost on why.
Middle-ware js file
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function isUser(login){
var UsersSchema = new Schema({
user: String,
user_type: String,
password: String,
first_name: String,
middle_name:String,
last_name: String,
birth_date: Date,
join_date: Date
});
var UserModel = mongoose.model('users', UsersSchema);
mongoose.connect('mongodb://localhost/identity');
mongoose.model('users', UsersSchema);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error: '));
db.once('open', function cb () {
UserModel.findOne({'user': login}, function (err, user){
if (err){ throw err; }
console.log('connected to user db and preformed lookup.');
console.log(user);
return user;
});
});
}
module.exports.authenticate = function (login, password, cb) {
var user = isUser(login),
msg;
console.log(user);
if (!user) {
msg = 'Invalid User';
cb(null, msg);
return;
}
if (user.password != password) {
msg = 'Invalid Password';
cb(null, msg);
return;
}
cb(user, null);
};
Console Output
undefined
Login Failed! : Invalid User
connected to user db and preformed lookup.
{ _id: 51c8e16ce295c5b71ac6b229,
user: 'bclark#themindspot.com',
user_type: 'admin_master',
password: 'enter',
first_name: 'Brandon',
middle_name: 'Laurence',
last_name: 'Clark',
birth_date: Fri Mar 19 1982 00:00:00 GMT-0800 (PDT),
join_date: Wed Jun 26 2013 00:00:00 GMT-0700 (PDT) }

db.once and UserModel.findOne are asynchronous functions, hence why you provide an anonymous function that is called when they are done. If you want your isUser function to 'return' the results of these asynchronous functions you will have to make it use a callback as well.
Replace
function isUser(login){
with
function isUser(login, callback){
and
return user;
with
callback(user).
It is also recommended to not throw errors in asynchronous code, but pass them on with the callback, similar to what db.once and UserModel.find do, like so:
Remove
if (err){ throw err; }
and replace the callback above with
callback(err, user);
While you're at it, since you don't do anything with the error or user anymore, you might as well call UserModel.findOne({'user': login}, callback);
===
The complete thing would become the following. Note that I follow the callback(err, result) convention.
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function isUser(login, callback){
var UsersSchema = new Schema({
user: String,
user_type: String,
password: String,
first_name: String,
middle_name:String,
last_name: String,
birth_date: Date,
join_date: Date
});
var UserModel = mongoose.model('users', UsersSchema);
mongoose.connect('mongodb://localhost/identity');
mongoose.model('users', UsersSchema);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error: '));
db.once('open', function cb () {
UserModel.findOne({'user': login}, callback);
});
}
module.exports.authenticate = function (login, password, cb) {
var user = isUser(login, function(err, user) {
if (err) {
cb(err);
}
console.log(user);
if (!user) {
msg = 'Invalid User';
cb(msg);
return;
}
if (user.password != password) {
msg = 'Invalid Password';
cb(msg);
return;
}
cb(null, user);
});
};
Finally, consider using (custom) Error objects instead of String messages, google it for explanations as to why.

yes this is a async issue. you may know that node.js every action has a separate thread
in your code you call
var user = isUser(login),
according to You it should return the result from the isUser function but the execution give this function a separate thread and it goes on to the next statement.
as on the next statement the user is undefined because it doesn't return anything from the function isUser
so the statement if (!user) { becomes true
to avoid this error you should place isUser in a callback function
means restrict the execution until and unless you get response from the function

Related

How to fix "User.findOne()" returning null for user

I'm currently working on a new project and I'm trying to get the login route working. But the login always fails because the User.findOne() method is always returning as null.
I've tried changing the export from the usermodel to
var User = mongoose.model('User', UserSchema, 'User');
But it hasn't change anything.
I know the connection to the database is fine because the register route works fine and saves correctly.
Login Route
router.post('/login', function (req, res) {
User.findOne({ username: req.body.username, password: req.body.password }, function (err, user) {
if (err || user == null) {
res.redirect('/login');
console.log(user);
} else {
req.session.userId = user._id;
res.redirect('/');
}
});
});
User Schema
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
}
});
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email }).exec(function (err, user) {
if (err) {
return callback(err);
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, hash, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
});
});
};
//hashing a password before saving it to the database
UserSchema.pre('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
var User = mongoose.model('users', UserSchema);
module.exports = User;
Database Connection
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/wowDb', { useNewUrlParser: true });
var db = mongoose.connection;
mongoose.set('useCreateIndex', true);
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () { });
I used
db.users.findOne({username: "Imortalshard"});
and got the output
{
"_id" : ObjectId("5cc10cd13361880abc767d78"),
"email" : "admin#wowdb.com",
"username" : "Imortalshard",
"password" : "$2b$10$7Ln5yHFqzPw/Xz6bAW84SOVhw7.c0A1mve7Y00tTdaKzxzTph5IWS",
"__v" : 0
}
Console output from console.log(req.body) and console.log(user)
I'm waiting the user that i registered to be able to successfully login, but currently it is just redirecting me back to the login page and giving me a null reading for user in the console.

MongoDB update if user not exists

I have a problem to update user if his/her name is not available in my database
I thought if my function "User.findOne" doesn't find a user in my mongodb it can update database. Unfortunately nothing happens. I get only output "Hello Anna you are new here!" My name is not saved into my mongodb
Could somebody smart give me please a tip how can I save username if it is not in my database
var User = require('./user');
var myName = this.event.request.intent.slots.first_name.value;
self = this;
User.findOne({ name: myName }, function(err, user) {
if (err ||!user){
var userSave = new User({
name: myName
});
userSave.save(function (err, results) {
console.log(results);
self.emit(':ask',
"Hello "+ myName +"you are new here!")
});
}
else {
self.emit(':ask',
"Hello "+ myName +" you are not new!")
}
});
My mongoose model code:
//user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect("mongodb://c******mlab.com:***/users");
var userSchema = new Schema({
name: String,
userId: { type: String, required: false, unique: true }
});
var User = mongoose.model('User', userSchema);
module.exports = User;
var User = require('./user');
var myName = this.event.request.intent.slots.first_name.value;
self = this;
User.findOne({
name: myName
}, (err, user) => {
if(err) throw err;
if(user) {
self.emit(':ask', `Hello ${myName} you are not new`);
} else {
User.create({
name: myName
}, (err, result) => {
if(err) throw err;
console.log(result);
self.emit(':ask', `Hello ${myName} you are new here!`);
})
}
});
this should work.
The line if (err || !user) is confusing to read, and in this style you're mixing error handling (if (err)) and a condition in your code that you expect to hit (if (!user)). I suggest you separate them so the code is easier to read and debug.
For example, using plain Javascript and the MongoDB node driver:
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect('mongodb://localhost/test', function(err, conn) {
// connection error handling
if (err) {
console.log('Connection error: ' + err);
}
conn.db('test').collection('test').findOne({name:'abc'}, function(err, doc) {
// findOne error handling
if (err) {
console.log('Error: ' + err);
}
// if document exists
if (doc) {
console.log('Document found: ' + JSON.stringify(doc));
}
// if document doesn't exist
else {
console.log('Document not found');
}
conn.close();
});
});
If the database contains the user abc, the output would be:
$ node script.js
Document not found
If the user abc exists:
$ node script.js
Document found: {"_id":0,"name":"abc"}
I believe using a similar pattern you can modify your code to do what you need.

passport-local-mongoose set new password after checking for old password

I am using passportjs to handle auth of my app.
Once the user is logged in, I want to add the possibility to change the password from inside the app.
this is in my controller:
$http.post('/change-my-password',{oldPassword: $scope.user.oldpassword, newPassword: $scope.user.newpassword})
.then(function (res) {
if (res.data.success) {
// password has been changed.
} else {
// old password was wrong.
}
});
and this is my route handler in express nodejs in backend:
router.post('/change-my-password', function (req, res) {
if (!req.isAuthenticated()) {
return res.status(403).json({
success: false
});
}
UserSchema.findById(req.user._id, function(err, user){
if (err) return res.status(200).json({success: false});
user.validatePassword(req.body.oldPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
user.setPassword(req.body.newPassword, function() {
if (err || !user) {
return res.status(200).json(
{
success: false
}
)
}
user.save(function(err) {
if (err) return res.status(200).json({success: false});
req.login(user, function (err) {
if (err) return res.status(200).json({success: false});
return res.status(200).json({success: true});
});
});
});
});
});
});
here is my user schema model:
// user model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new Schema({
email: String,
password: String,
confirmStatus: Boolean,
token: String,
registerAt: Number
});
UserSchema.methods.validatePassword = function (password, callback) {
this.authenticate(password, callback);
};
UserSchema.plugin(passportLocalMongoose,
{
usernameField: 'email'
});
module.exports = mongoose.model('users', UserSchema);
the problem:
I find my user by Id in my mongoose schema UserSchema then I should check if the oldPassword is valid or not, and then I set the new password.
I successfully find the user and the set the new password. But the part that should check for comparison of the old password field, doesn't work at all. Whatever I enter in the old password field gets accepts as OK and that step is skipped. Whereas, it should throws an error saying that the old password is wrong.
I am also advised to use sanitizedUser in order not to show my salt and etc.
Question is: how can I first do the comparison check of the old password and then do the set new password step? If possible, how can I use the sanitize? And how can I check if the user is not entering the same password as the new password? or if possible, saying that the new password is very similar to the old one?
You can implement the it using the new feature added 3 days ago:
just use the changePassword method, and it handles it through this:
schema.methods.changePassword = function(oldPassword, newPassword, cb) {
if (!oldPassword || !newPassword) {
return cb(new errors.MissingPasswordError(options.errorMessages.MissingPasswordError));
}
var self = this;
this.authenticate(oldPassword, function(err, authenticated) {
if (err) { return cb(err); }
if (!authenticated) {
return cb(new errors.IncorrectPasswordError(options.errorMessages.IncorrectPasswordError));
}
self.setPassword(newPassword, function(setPasswordErr, user) {
if (setPasswordErr) { return cb(setPasswordErr); }
self.save(function(saveErr) {
if (saveErr) { return cb(saveErr); }
cb(null, user);
});
});
});
};
so in your code, you need to replace the validatePassword method by this:
user.changePassword(req.body.oldPassword,req.body.newPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
hope this works for you.

Nodejs export not returning value

I have a nodejs export like this
exports.add = function(req){
var newUser = new User({
email: req.body.email,
password: req.body.password,
});
// Attempt to save the user
newUser.save(function(err) {
if (err) {
return true;
}
return false;
});
}
But it's giving as undefined when i call the function like this
var value = instance.add(req);
Here instance is the imported instance of the javascript file
As stated in comments by #Ben Fortune you couldn't simply return a value from an asynchronous function call. you should use callbacks or promises:
The callback way:
exports.add = function (req, callback) {
var newUser = new User({
email: req.body.email,
password: req.body.password,
});
// Attempt to save the user
newUser.save(function(err) {
if (err) {
callback(err, null);
}
callback(null, newUser.toJSON()) ;
});
}
Then:
instance.add(req, function(err, value) {
if (err) throw err;
// use value here
});
Read More: How do I return the response from an asynchronous call? And implement promise way if you prefer.

mongoosejs find specific record

i want to find a specific record in mongoose. i'm trying to make a login/register form and im kind of new to node.js..
for now i have this code:
app.post('/register', function(request, response)
{
console.log('REGISTER OK');
//set connection to mongodb.
mongoose.connect('mongodb://localhost');
var db = mongoose.connection;
//Connect to mongodb.
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback()
{
console.log('connection succeed.');
//Create User schema.
var userSchema = mongoose.Schema(
{
username: String,
password: String
});
//User model.
var UserModel = mongoose.model('User', userSchema);
var user = new UserModel(
{
username: request.body.username,
password: request.body.password
});
user.save(function(error, data)
{
if(error)
console.log(error);
else
{
response.render('register',
{
'Title': Title,
'result': data
});
UserModel.find(function(error, data)
{
if(error)
console.log(error);
else
console.log(data);
});
}
});
});
});
i dont quite get the find method of mongoose, i just need to find the username in order to do some function, is there a function that moongose has to avoid record if this exists already?
UserModel.findOne ({ name: "someName" }, function (err, user) {
if (err) console.log (err);
if (!user) console.log ('user not found');
// do something with user
});

Resources