Why am I getting Promise Pending despite using await in my controller? - javascript

I have a repository where I connect directly to my model to insert some data, it creates the data successfully but when I connect my controller to this repository, I get a nulled response, if I log it in the repository itself I get Promise . Please checkout my code below:-
Repository.js
exports.register = (request) => {
const data = UserModel.findOne({email: request.email})
.then(user => {
if(user)
{
return {status: 400, message: 'Email Already exist'}
} else {
return bcrypt.genSalt(10, (err, salt) => {
const newUser = new UserModel({
username: request.username,
email: request.email,
password: request.password
});
return bcrypt.hash(newUser.password, salt, async (err, hash) => {
if(err) throw err;
newUser.password = hash;
return newUser.save()
.then(user => {
const token = jwt.sign({id: user._id}, process.env.JWT_SECRET, {
expiresIn: 86400 // expires in 24 hours
});
return {status: 200, message: 'Successfully Registered', auth: true, token: token, user: user}
})
.catch(err => {
return {status: 400, message: err}
})
})
})
}
})
console.log(data) // This part is return Promise <pending>
return data;
};
Controller.js
exports.SeedRegisteration = async (req, res, next) => {
try {
let element = await userRepo.register({username: "Testin", email: "Testin#test.com", "password":
"joe" });
return await res.status(200).json({ status: 200, data: element })
} catch (e) {
return res.status(400).json({ status: 400, message: e.message });
}
};
Works fine but does not return data

Here's the register function using the Promise version of bcrypt (if you don't supply a callback, the bcrypt functions return a Promise
exports.register = (request) =>
UserModel.findOne({
email: request.email
})
.then(user => {
if (user) {
throw 'Email Already exist'
}
})
.then(() => bcrypt.genSalt(10))
.then(salt => {
const newUser = new UserModel({
username: request.username,
email: request.email,
password: request.password
});
return bcrypt.hash(newUser.password, salt)
.then((hash) => {
newUser.password = hash;
return newUser.save();
})
}).then(user => {
const token = jwt.sign({
id: user._id
}, process.env.JWT_SECRET, {
expiresIn: 86400 // expires in 24 hours
});
return {
status: 200,
message: 'Successfully Registered',
auth: true,
token: token,
user: user
}
}).catch(err => {
return {
status: 400,
message: err
}
});
Note: there is ONE nested .then - this code could be perfectly flat if you used async/await in register - however I was not prepared to perform such a big rewrite for the answer. Now that the code is in a nice almost flat promise chain, it's relatively simple to convert the whole thing into async/await style

There are too many return statements which return promise. Please update your code in to the following:
exports.register = (request) => {
return new Promise((resolve, reject) => {
try {
UserModel.findOne({ email: request.email })
.then(user => {
if (user) {
return reject({ status: 400, message: 'Email Already exist' })
} else {
bcrypt.genSalt(10, (err, salt) => {
const newUser = new UserModel({
username: request.username,
email: request.email,
password: request.password
});
bcrypt.hash(newUser.password, salt, async (err, hash) => {
if (err) return reject(err);
newUser.password = hash;
newUser.save()
.then(user => {
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, {
expiresIn: 86400 // expires in 24 hours
});
return resolve({ status: 200, message: 'Successfully Registered', auth: true, token: token, user: user })
})
.catch(err => {
return reject({ status: 400, message: err })
})
})
})
}
}).catch(err => {
return reject(err)
})
} catch (error) {
return reject(error)
}
});
};

Related

trying to get single item from list

I have a fairly bare bones mern stack and im trying to call getUsers and then retrieve a single user from the returned list of users.
however using [] doesnt seem to work. It looks like getUsers correctly returns the list of users but idk how to pull a single one out
user-ctrl.js
const User = require('../models/user-model')
createUser = (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a user',
})
}
const user = new User(body)
if (!user) {
return res.status(400).json({ success: false, error: err })
}
user
.save()
.then(() => {
return res.status(201).json({
success: true,
id: user._id,
message: 'User created!',
})
})
.catch(error => {
return res.status(400).json({
error,
message: 'User not created!',
})
})
}
updateUser = async (req, res) => {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'You must provide a body to update',
})
}
User.findOne({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(404).json({
err,
message: 'User not found!',
})
}
user.name = body.name
user.email = body.email
user
.save()
.then(() => {
return res.status(200).json({
success: true,
id: user._id,
message: 'User updated!',
})
})
.catch(error => {
return res.status(404).json({
error,
message: 'User not updated!',
})
})
})
}
deleteUser = async (req, res) => {
await User.findOneAndDelete({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!user) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: user })
}).catch(err => console.log(err))
}
getUserById = async (req, res) => {
await User.findOne({ _id: req.params.id }, (err, user) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!user) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: user })
}).catch(err => console.log(err))
}
getUsers = async (req, res) => {
await User.find({}, (err, users) => {
if (err) {
return res.status(400).json({ success: false, error: err })
}
if (!users.length) {
return res
.status(404)
.json({ success: false, error: `User not found` })
}
return res.status(200).json({ success: true, data: users })
}).catch(err => console.log(err))
}
module.exports = {
createUser,
updateUser,
deleteUser,
getUsers,
getUserById,
}
You need to actually call the getUsers function (with parenthesis), and then wait for the promise to resolve, with await
var allUsers = await UserCtrl.getUsers();
var defaultUser = allUsers[0];
or
UserCtl.getUsers()
.then(u=>u[0])
.then(user=>{
// insert code that uses the user here
})
It's a promise, so try with async/await
var allUsers = await UserCtrl.getUsers();
var defaultUser = allUsers[0];
To make await work, put async infront of your method:
async createUser = (req, res) => {

How to use chained Promises calls?

I'm trying to implement a simple login function.
module.exports.login = (req, res, next) => {
let loggedin_user;
User.findOne({email: req.body.email.toLowerCase()})
.then(user => {
if(!user){
throw ('Invalid e-mail or password');
}
loggedin_user = user;
return bcryptjs.compare(req.body.password, user.password)
})
.then(res => {
if(!res){
return res.status(401).json('Invalid e-mail or password')
}
const token = jwt.sign({
id: loggedin_user._id,
role: loggedin_user.role
}, process.env.JWT_KEY, { expiresIn: '24h' });
return res.status(200).json({
token: token,
role: loggedin_user.role,
expires_in: 24*60*60})
})
.catch(err => {
return res.status(401).json(err);
})
}
My code works great until it reaches the last return part, this part:
return res.status(200).json({
token: token,
role: loggedin_user.role,
expires_in: 24*60*60,
})
It doesn't return anything instead it jumps to the catch block, although it console logs that javascript object that I need to return, it logs it right before the return statement.
What's the problem?
You should log the error message in the catch to see what the error is.
I'd suspect req.body or user may be undefined, and checking the properties email and password could result in an error.
At first glance, I don't catch any error to your code. Maybe issue is with your password, but I am not sure.
Any way to simplify things and check the same, I modified your code as below. Try this and let me know the output. To deal with Promises, Async/Await is better.
module.exports.login = async (req, res, next) => {
try {
const user = await User.findOne({ email: req.body.email.toLowerCase() });
if (!user) {
res.status(401).json('Invalid e-mail');
}
const checkPass = await bcryptjs.compare(req.body.password, user.password);
if (!checkPass) {
res.status(401).json('Invalid password');
}
const token = jwt.sign(
{
id: user._id,
role: user.role,
},
process.env.JWT_KEY,
{ expiresIn: '24h' }
);
res.status(200).json({
token: token,
role: user.role,
expires_in: 24 * 60 * 60,
});
} catch (err) {
console.error(err.message);
}
};

Object is returned the same although I'm mutating it

The upcoming code snippet is removing the password attribute from the user JSON object and return it in response. what is happening is that the password attribute is still returning!
const signin = (req, res, next) => {
let requestBody = req.body;
userModel.findUserByEmail(requestBody.email).then(user => {
bcrypt.compare(requestBody.password, user.password, (error, result) => {
if (!result) {
return res.status(500).json({
status: false,
message: 'Auth Failed!',
error
});
}
if (error) {
return res.status(500).json({
error
});
}
let token = jwt.sign({
email: user.email,
userId: user._id
},
process.env.JWT_KEY,
{
expiresIn: "2h"
});
// remonve password key
delete user.password
res.status(200).json({
status: true,
message: 'Authenticated!',
data: {
token,
user
}
});
});
}).catch(error => {
return res.status(500).json({
status: false,
message: 'Auth Failed!',
error
});
});
}
not sure the problem is related to async compilation or not
You could create a new object without the password and use that in your response:
const { password, ...restOfUser } = user
res.status(200).json({
status: true,
message: 'Authenticated!',
data: {
token
user: restOfUser
}
})

.then is undefined when I try to save in MongoDB

I'm new to node.js.
I got this message:
TypeError: Cannot read property 'then' of undefined
Here is the code:
router.post("/signup", (req, res) => {
const userRegister = new UserRegister({
_id: new mongoose.Types.ObjectId(),
nickname: req.body.nickname,
email: req.body.password,
password: req.body.password,
level: 0
});
console.log(req.body.nickname + " " + req.body.email + " " + req.body.password);
userRegister
.save()
.then(doc => {
console.log(doc);
res.status(200).json({
message: "User created"
});
})
.catch(err => {
if (err)
console.log("error => " + err)
res.status(409).json({
message: "ERROR"
})
});
});
and the Schema:
const mongoose = require("mongoose");
const userRegister = mongoose.Schema({
_id : mongoose.Schema.Types.ObjectId,
nickname: String,
email: String,
password: String,
level: Number
});
module.exports = mongoose.model("UserRegister", userRegister);
I don't really understand why it says ".then undefined".
(the body is good)
It seems like function "save" does not return Promise. But in source code it does...
https://github.com/Automattic/mongoose/blob/master/lib/model.js#L385
Also you can try "create" method.
https://github.com/Automattic/mongoose/blob/master/lib/model.js#L2646
Maybe it will be hellpfull:
const result = new SomeModel({...});
new Promise((resolve, reject) => {
// Save model
result.save(err => {
if (err) {
return reject(new Error(`Error with exam ersult save... ${err}`));
}
// Return saved model
return resolve(result);
})
.then(res => {
return res;
})
.catch(err => {
throw new Error(err);
});
check if the model is the promising type or not if this is not the promising then use callback
mongoose promises
assert.ok(user instanceof Promise); // this return ture or false
router.post("/signup", (req, res) => {
const userRegister = new UserRegister({
_id: new mongoose.Types.ObjectId(),
nickname: req.body.nickname,
email: req.body.password,
password: req.body.password,
level: 0
});
console.log(req.body.nickname + " " + req.body.email + " " + req.body.password);
var user = userRegister.save()
assert.ok(user instanceof Promise);
// if assert.ok return true then use user.then
// else use callback userRegister.save( function(doc){ })
user.then(doc => {
console.log(doc);
res.status(200).json({
message: "User created"
});
})
.catch(err => {
if (err)
console.log("error => " + err)
res.status(409).json({
message: "ERROR"
})
});
});
router.post("/signup", async (req, res) => {
try{
const userRegister = await UserRegister.create({
_id: new mongoose.Types.ObjectId(),
nickname: req.body.nickname,
email: req.body.password,
password: req.body.password,
level: 0
});
}
catch(err){
console.log("error => " + err)
res.status(409).json({
message: "ERROR"
})
}
console.log(userRegister);
res.status(200).json({
message: "User created"
});
});

Cannot set headers after they are sent to the client with mongoose and node.js

I'm trying to make a registration function for users. I want to check first if the email exist, if so return a json
{ message: 'cannot register a new user' }
or else a json with confirmation and registered user details.
this code works fine, however the compiler says:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
its seems the the problem is this line:
res.status(200).json({ message: 'A new user was created!', user: result });
but I don't know how fix it so it won't make this message
my code is:
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
exports.signup = async (req, res, next) => {
const firstname = req.body.firstname;
const lastname = req.body.lastname;
const email = req.body.email;
const password = req.body.password;
try {
let canRegister = await User.findOne({ email: email })
.then(user => {
if (!user) {
return true;
}
res.status(400).json({ message: 'Email is already in use' });
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
let addUser = await bcrypt
.hash(password, 12)
.then(hashedPw => {
const user = new User({
firstname: firstname,
lastname: lastname,
email: email,
password: hashedPw
});
return user.save();
})
.then(result => {
res.status(200).json({ message: 'A new user was created!', user: result });
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
} catch {
res.status(400).json({ message: 'Email is already in use' });
}
};
You are trying to send the response twice.
let canRegister = await User.findOne({ email: email })
.then(user => {
if (!user) {
return true;
}
// You might have executed this 1st - and continue.
res.status(400).json({ message: 'Email is already in use' });
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
But your code doesn't exit. It moves onto the next block.
let addUser = await bcrypt
.hash(password, 12)
.then(hashedPw => {
const user = new User({
firstname: firstname,
lastname: lastname,
email: email,
password: hashedPw
});
return user.save();
})
.then(result => {
// Then you are sending status again with this line.
res.status(200).json({ message: 'A new user was created!', user: result });
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
You should figure out after canRegister is assigned (await block is complete) and return appropriately before proceeding to the next block.
Something like this:
let canRegister = await User.findOne({ email: email })
.then(user => {
if (!user) {
return true;
}
return false;
// Don't use the res.status here.
})
.catch(err => {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
});
if (!canRegister) {
return res.status(400).json({ message: 'Email is already in use' });
}

Categories

Resources