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
}
}
Related
I am building a Node.js project which has an advanced folder structure for future purposes.
user.register controller:
exports.register = async (req, res) => {
try {
var isValidated = await userService.validateInDatabase(req);
if (!isValidated)
return res
.status(409)
.json({ error: "Phone number or email is already registered" });
var user = await userService.create(req);
return user
} catch (e) {
console.trace(e);
return res.status(400).json({ message: e.message });
}
};
The services file code:
exports.create = async (user) => {
const hashedPassword = passwordHash.generate(user.password);
let new_user = new User({
phoneNumber,
email,
password: hashedPassword,
});
const payload = {
id: new_user._id,
};
let token = jwt.sign(payload, keys.JWToken, { expiresIn: 31556926 });
const userData = await new_user.save();
return userData;
};
exports.validateInDatabase = async (req) => {
let check_user = await User.findOne({
$or: [{ email: req.body.email }, { phoneNumber: req.body.phoneNumber }],
});
if (check_user) return false;
return true;
};
Now, whenever I send the request from the postman it says invalid password Why is that?
I was trying to separate the logic of my back-end server into route/controller/service,
but one problem is when I try to catch error in service layer, I will return error, but how can I determine if it's error and return 404 status in my controller layer ?
Here's the code
Service layer
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return savedUser
} catch (err){
return err
}
};
Controller layer
const register = (req,res) => {
const { name, email, password } = req.body
const user = UserService.register(name,email,password);
if(how to determine here?){
res.status(201).json(user);
}
}
You could return an object in your Service Layer instead of an User or Error object:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return { user: savedUser, error: null }
} catch (error){
return { error, user: null }
}
};
In your Controller Layer you would then do something like this:
const register = (req,res) => {
const { name, email, password } = req.body
const { user, error } = UserService.register(name,email,password);
if(error){
res.status(500).json({ user, error: error.message });
} else {
res.status(200).json({ user, error })
}
}
return the save promise and handle errors in your controller
serivce:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
return newUser.save();
};
controller:
const register = async (req,res) => {
const { name, email, password } = req.body
try {
const user = await UserService.register(name,email,password);
res.status(201).json(user);
} catch(error) {
// handle error here
}
}
User.findOne({ email: email })
.then((savedUser) => {
if (savedUser) {
return res
.status(422)
.json({ error: "user already exists with that email" });
}
bcrypt.hash(password, 12).then((hashedpassword) => {
const user = new User({
email,
password: hashedpassword,
name,
pic,
});
user
.save()
.then((user) => {
res.json({ message: "saved successfully" });
})
.catch((err) => {
console.log(err);
});
});
})
.catch((err) => {
console.log(err);
});
});
I'm studying async/await by changing some callback function to async/await. This code has two catch error but I don't know how to change this code to async/await.
const saveUser = async (email,password,name,pic)=>{
try{
const user = new User({
email,
password: await bcrypt.hash(password, 12),
name,
pic,
})
const savedUser = await User.save()
return savedUser
}catch(error){
throw new Error(error)
}
}
const findUser = async (email)=>{
try{
const user = await User.findOne({ email: email })
return user
}catch(error){
throw new Error(error)
}
}
A key thing to remember is that you can await on Promise (the async keyword is like a decorator that force a function to return a Promise).
The following methods returns promise so you can use await
User.findOne(...)
bcrypt.hash(...)
user.save()
the refactored code will look something like this:
async function functionName(...) {
try {
const savedUser = await User.findOne({ email: email });
}
catch (e) {
//...
}
if (savedUser) {
return res
.status(422)
.json({ error: "user already exists with that email" });
}
const hashedpassword = await bcrypt.hash(password, 12)
const user = new User({
email,
password: hashedpassword,
name,
pic,
});
try {
await user.save();
}
catch (e) {
return res
.status(422)
.json({ error: "...ERROR..." });
}
res.json({ message: "saved successfully" });
}
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 am writing code in Node.js to encrypt passwords using bcrypt.
However, if you use bcrypt, you will get an ValidationError: User validation failed: password: Cast to String failed for value "Promise { <pending> }" at path "password"
I do not get this error if I save it as plain text without encryption.
Is there a secret of bcrypt I do not know?
bcrypt (not working)
const bcrypt = require('bcrypt');
sign_up = (req, res, next) => {
const { email, password } = req.body;
const User = User.findOne({ email: email });
if (exUser) {
return res.send('exist user');
}
const hash = bcrypt.hash(password, 8);
const user = new User({
email: email,
password: hash
});
user.save((err) => {
if (err) {
return next(err);
}
res.send('signup success');
});
};
no bcrypt (working)
sign_up = (req, res, next) => {
const { email, password } = req.body;
const User = User.findOne({ email: email });
if (exUser) {
return res.send('exist user');
}
const user = new User({
email: email,
password: password
});
user.save((err) => {
if (err) {
return next(err);
}
res.send('signup success');
});
};
To elaborate on Chris's comment:
It appears that bcrypt.hash is asynchronous, and is returning a Promise.
To fix this, I would recommend using an async function and awaiting the result. MDN page
This may require a newer version of NodeJS than what you are running.
const bcrypt = require('bcrypt');
// Async function allows us to use await
sign_up = async (req, res, next) => {
const { email, password } = req.body;
const User = User.findOne({ email: email });
if (exUser) {
return res.send('exist user');
}
// We await the result of the hash function
const hash = await bcrypt.hash(password, 8);
const user = new User({
email: email,
password: hash
});
user.save((err) => {
if (err) {
return next(err);
}
res.send('signup success');
});
};
Do not use the bcrypt.hashSync function, as while it is running your server will not be able to do anything else.