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.
Related
I have a user register function which is using async/await and I need to implement some code from another API. When I try to integrate it I get an error that I can't use await outside an async function.
exports.register = async (req, res) => {
// some logic here
nexmo.verify.request(
{
number: formattedMobile,
brand: "My Brand",
code_length: "4",
},
(err, result) => {
if (err) {
// If there was an error, return it to the client
return res.status(500).send(err.error_text);
}
// Otherwise, send back the request id. This data is integral to the next step
const requestId = result.request_id;
const salt = await bcrypt.genSalt(12);
const hashedPassword = await bcrypt.hash(password, salt);
const createdUser = new User({
name: name,
email: email,
mobile: formattedMobile,
password: hashedPassword,
});
try {
await createdUser.save();
res.status(200).send({ user: createdUser._id, otp: requestId });
} catch (err) {
res.status(500).send(err);
}
}
You need to make the callback function async, and most likely wrap the entire code in a try catch block to handle errors.
async (err, result) => {
if (err) {
// If there was an error, return it to the client
return res.status(500).send(err.error_text);
}
try {
// Otherwise, send back the request id. This data is integral to the next step
const requestId = result.request_id;
const salt = await bcrypt.genSalt(12);
const hashedPassword = await bcrypt.hash(password, salt);
const createdUser = new User({
name: name,
email: email,
mobile: formattedMobile,
password: hashedPassword,
});
try {
await createdUser.save();
res.status(200).send({ user: createdUser._id, otp: requestId });
} catch (err) {
res.status(500).send(err);
}
} catch(err) {
console.log(err);//do whatever error handling here
}
}
I am experimenting with node authentication, I have managed to store a username and a hashed password into my database, but I want to return the json back without the hashed password.
I am deleting the password key before sending the JSON back but the password still shows in the returned result.
router.post("/signup", async (req, res, next) => {
const user = await User.exists({ username: req.body.username });
if (user) {
const error = new Error("Username already exists");
next(error);
} else {
const newUser = new User({
username: req.body.username,
password: req.body.password,
});
try {
const result = await newUser.save();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
}
});
the User model has a pre hook to hash the password before save:
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
try {
user.password = await bcrypt.hash(user.password, 12);
return next();
} catch (err) {
return next(err);
}
});
Here is the solution thanks to Mahan for pointing it out.
result returns a Mongoose object so needs turning into a normal Javascript object first.
try {
let result = await newUser.save();
result = result.toObject();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
this is my piece of code:
i declared a variable(newSeller) and i expect to use it in the process
let newSeller = '';
if (req.body.selectSeller == '') {
User.findOne({email: req.body.sellerEmail}).then(userEx => {
if (!userEx) {
const newUser = new User({
firstName: req.body.sellerName,
lastName: req.body.sellerLastName,
title: req.body.sellerTitle,
phoneNumber: req.body.sellerPhNum,
email: req.body.sellerEmail,
password: req.body.password,
address: req.body.sellerAddress
});
bcrypt.genSalt(10, (err, salt)=>{
bcrypt.hash(newUser.password, salt, (err, hash)=>{
newUser.password = hash;
});
});
newUser.save().then(savedSeller => {
newSeller = savedSeller.id;
});
} else if (userEx) {
req.flash('error_message', 'this email already exists. try another one')
res.redirect('/admin/invoice/incoming');
}
});
} else {
newSeller = req.body.selectSeller;
}
this piece of code actually saves the expected document successfully but when i assign the variable (newSeller) to the value of ES6 promise (after then() ) it doesn't work!
could you please help me with this?
how can i fetch the saved user values?
Basically you are using async and sync functions together in various places which messes up everything. Basically you cannot use sync functions if you use even one async function in the entire module. But then again in async functions, Try to use promise based syntaxes or async-await
Assuming you are using the code in some express route here is how you can simplify the code(commented for understanding):
app.post('/someroute', async (req, res) => { //<<-Async handler
let newSeller = '';
if (req.body.selectSeller == '') {
try { //<<--need to catch `async-await` errors
const userEx = await User.findOne({ email: req.body.sellerEmail });//<<awaiting the result
if (!userEx) {
const newUser = new User({
firstName: req.body.sellerName,
lastName: req.body.sellerLastName,
title: req.body.sellerTitle,
phoneNumber: req.body.sellerPhNum,
email: req.body.sellerEmail,
password: req.body.password,
address: req.body.sellerAddress
});
const salt = await bcrypt.genSalt(10); // await
const hash = await bcrypt.hash(newUser.password, salt);// await
newUser.password = hash;
const savedSeller = await newUser.save(); //await
newSeller = savedSeller.id;
} else {
req.flash('error_message', 'this email already exists. try another one')
res.redirect('/admin/invoice/incoming');
}
} catch (err) { //<<--if there is an error
req.flash(...something...)
res.redirect(...somewhere...);
}
} else {
newSeller = req.body.selectSeller;
}
//do something with newSeller
})
I'm trying to create a function in a file to return a promis, which I will call form another file. I'm trying to use the 'util.promisify' to wrap the function, but I'm getting an error. Here is the code and the error:
from my 'checkEmail.js':
const Profile = require('../../models/profile');
const util = require('util');
var exports = module.exports = {};
exports.findEmail = util.promisify(checkEmail());
function checkEmail (email) {
Profile.findOne({ 'emails': { $elemMatch: { email_address: email } } }, (err, userEmail) => {
let conclusion = false;
if (err) {
console.log('Error in looking up an existing email');
} else {
if (userEmail) {
console.log('We found an existing owner for email: ' + email);
conclusion = true;
}
}
return conclusion;
})
}
Calling it on 'profile.js':
router.route('/addemail/:id')
// ADD EMAILS
.put(function (req, res) {
Profile.findOne({ 'owner_id': req.params.id }, function (err, profile) {
if (err)
res.send(err);
EmailCheck.findEmail(req.body.email_address).then((data)=>{
console.log('The answer is: ', data);
});
profile.emails.push({
email_type: req.body.email_type,
email_address: req.body.email_address
})
profile.save(function (err) {
if (err)
res.send(err);
res.json(profile);
});
});
});
The error I'm getting is:
Config for: http://localhost:3000
internal/util.js:272
throw new ERR_INVALID_ARG_TYPE('original', 'Function', original);
Any help would be appreciated.
In order to promisify the function that you pass to util.promisify must:
Take a function following the common error-first callback style, i.e.
taking a (err, value) => callback as the last argument, and returns a version that returns promise
So you can either promisify Profile.findOne, or pass a callback as the last argument to checkEmail
function checkEmail (email, callback) {
Profile.findOne({ 'emails': { $elemMatch: { email_address: email } } }, (err, userEmail) => {
let conclusion = false;
if (err)
return callback(new Error('Error in looking up an existing email'));
if (userEmail) {
console.log('We found an existing owner for email: ' + email);
conclusion = true;
}
return callback(null, conclusion);
})
}
And then you should call it like this:
exports.findEmail = util.promisify(checkEmail);
Otherwise you're passing to .promisify the returned value of checkEmail which is not a function following the style commented above.
You have typo, use util.promisify(checkEmail) instead, parentheses are redundant
Hi All,
I am authenticating my user using bcrypt module.
I am able to do perform the Registration process, but facing problem during Login process.
User Model:
var userSchema = new Schema({
email: {type: String, required: true},
password: {type: String,
});
Hashing methods:
userSchema.methods.encryptPassword = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(5), null)
};
userSchema.methods.validPassword = function (password) {
return bcrypt.compareSync(password, this.password);
};
Sign in:
module.exports.login = function (user, callback) {
User.findOne({'email': user.email, 'password': user.validPassword(this.password)}, callback);
};
Login Route
router.post('/login', function (req, res) {
var user = req.body;
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});
While executing am getting this error: TypeError:user.validPassword is not a function
Please Help.
Your mistake is that the user being provided to your login method is not a Mongoose DB object. Instead, your login function should look something like this:
module.exports.login = function (request, callback) {
User.findOne({'email': request.email }, function(err, user) {
if (err) return callback(err);
if(!user || !user.validPassword(request.password)) return callback();
return callback(null, user);
});
};
This will ensure that user is a valid Mongoose object before you attempt to verify the password.
One other possible solution, if you'd prefer to avoid checking that the password is valid in your data layer, is to simply fetch the user document based on its email and then check the password in the login route.
router.post('/login', function (req, res) {
var user = req.body;
User.findOne(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
if (!user.validPassword(req.body.password)) {
res.sendStatus(401);
return;
}
res.json(user.id);
});
});
In Login Route, you need to instantiate the Schema:
router.post('/login', function (req, res) {
var user = new User(req.body);
User.login(user, function (err, user) {
if (err) {
throw err;
}
if (!user) {
res.sendStatus(404);
return;
}
res.json(user.id);
});
});