I am writing an Express endpoint in Typescript to update a user's password in the database, but having some trouble with multiple queries/synchronous code.
My problem is right now I am trying to await the return of a function but it doesn't seem to be awaiting.
My route looks like this:
router.put("/:userId", validateUser, async (req: any, res: Response) => {
const result: any = await updatePassword(req.body.password, req.body.confirmPassword, req.params.userId);
console.log(result) //LOGS UNDEFINED????
if (result.status !== 200) res.status(result.status).send({ message: result.message, code: result.code });
res.send("Success!");
});
and that updatePassword function looks like this:
const updatePassword = async (password: string, confirmPassword: string, userId: string) => {
if (password !== confirmPassword) {
return { status: 409, message: Errors.mismatchingPasswordMessage, code: Errors.mismatchingPasswordCode };
}
const hashedPassword = bcrypt.hashSync(password, 10);
//update the password in the DB
pool.query("UPDATE users SET password = ? WHERE userId = ?", [hashedPassword, userId], (err: MysqlError, result: any) => {
if (err) {
console.log(err);
return { status: 500, message: err.message, code: err.code };
}
if (result.changedRows !== 1) {
return { status: 500, message: "Password could not be updated!" };
}
return { status: 200 };
});
}
Why is this the route not correctly awaiting the result of updatePassword??
Thanks in advance for any help.
The problem is, you're mixing async/await and callback style in this part
pool.query("UPDATE users SET password = ? WHERE userId = ?", [hashedPassword, userId], (err: MysqlError, result: any) => {
if (err) {
console.log(err);
return { status: 500, message: err.message, code: err.code };
}
if (result.changedRows !== 1) {
return { status: 500, message: "Password could not be updated!" };
}
return { status: 200 };
});
The function updatePassword jumps directly to the next line after this block, which is nothing. So it returns undefined.
You need to replace this block with async/await code. Something like :
try {
const result = await pool.query("UPDATE users SET password = ? WHERE userId = ?", [hashedPassword, userId]);
if (result.changedRows !== 1) {
return { status: 500, message: "Password could not be updated!" };
}
return { status: 200 };
}
catch (err){
console.log(err);
return { status: 500, message: err.message, code: err.code };
}
Related
I am not getting status message as 500 eventhough I set. always getting status message as 200. how to set the status as 500?
here is my code : "express": "4.17.2",
router.post('/register', async (req: Request, res: Response) => {
const { password, email } = req.body;
try {
const isUserExist = await UserModel.findOne({ email: email });
if (isUserExist) {
//status not set.
return res.json({ message: 'User already exist', success: false }).status(500);
}
const hashPassword = bcrypt.hashSync(password, 10);
req.body.password = hashPassword;
const newUser = new UserModel(req.body);
await newUser.save();
res.json({ message: 'user created successfully', success: true });
} catch (error) {
res.sendStatus(500).json({ message: 'Error creating user', success: false });
}
});
react axios:
when i use:
return res.status(500).json({ message: 'User already exist', success: false }); getting something went wrong
export const registerUser = createAsyncThunk('post/user', async (user: RegisterFormProps) => {
try {
const response = await axios.post(environment.BASE_URL + '/user/register', user);
console.log('suc', response.data.success);
if (response.data.success) {
toast.success(response.data.message);
} else {
toast.error(response.data.message);
}
} catch (error) {
const err = error as AxiosError;
console.log('err', err);
toast.error('something went wrong');
}
});
You should be using res.status instead of res.sendStatus in your code.
res.status(statusCode) just sets the status on the response.
whereas res.sendStatus(statusCode) sends the response after setting the status.
for example:
res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')
on the client side try using error.response.data in your catch block
Try this one:
router.post('/register', async (req: Request, res: Response) => {
const { password, email } = req.body;
try {
const isUserExist = await UserModel.findOne({ email: email });
if (isUserExist) {
//status not set.
return res.status(500).json({ message: 'User already exist', success: false });
}
const hashPassword = bcrypt.hashSync(password, 10);
req.body.password = hashPassword;
const newUser = new UserModel(req.body);
await newUser.save();
res.status(201).json({ message: 'user created successfully', success: true });
} catch (error) {
res.status(500).json({ message: 'Error creating user', success: false });
}
});
I've been struggling with Bcrypt on my MERN project I'm trying to create an authentication system I'm trying to run tests on Postman and I'm not sure why do I keep getting the error: "Illegal arguments: undefined, string at Object.bcrypt.hashSync"
this is my postman request:
this is the Controller Code:
const config = require("../config/auth.config");
const db = require("../models");
const User = db.user;
const Role = db.role;
var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
exports.signup = (req, res) => {
const user = new User({
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password, 8),
});
user.save((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (req.body.roles) {
Role.find(
{
name: { $in: req.body.roles },
},
(err, roles) => {
if (err) {
res.status(500).send({ message: err });
return;
}
user.roles = roles.map((role) => role._id);
user.save((err) => {
if (err) {
res.status(500).send({ message: err });
return;
}
res.send({ message: "User was registered successfully!" });
});
}
);
} else {
Role.findOne({ name: "user" }, (err, role) => {
if (err) {
res.status(500).send({ message: err });
return;
}
user.roles = [role._id];
user.save((err) => {
if (err) {
res.status(500).send({ message: err });
return;
}
res.send({ message: "User was registered successfully!" });
});
});
}
});
};
exports.signin = (req, res) => {
User.findOne({
username: req.body.username,
})
.populate("roles", "-__v")
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
var passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({ message: "Invalid Password!" });
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400, // 24 hours
});
var authorities = [];
for (let i = 0; i < user.roles.length; i++) {
authorities.push("ROLE_" + user.roles[i].name.toUpperCase());
}
req.session.token = token;
res.status(200).send({
id: user._id,
username: user.username,
email: user.email,
roles: authorities,
});
});
};
exports.signout = async (req, res) => {
try {
req.session = null;
return res.status(200).send({ message: "You've been signed out!" });
} catch (err) {
this.next(err);
}
};
The error message:
Illegal arguments: undefined, string at Object.bcrypt.hashSync wants to say that you're passing undefined as an argument to the hashSync function. We need to fix this error.
Take a closer look at this line where the error occurs:
password: bcrypt.hashSync(req.body.password, 8),
req.body.password is undefined, you can verify it by console.log(req.body.password). What's wrong is that you are sending data as URL parameters. So req.body is an empty object and req.body.password is undefined.
In Postman, select the Body tab, choose JSON format, then type your data as a JSON object. Then, in your code, use express.json() middleware to parse requests in JSON format. You'll have the desired output.
You can see my example request in Postman below:
While calling the service layer from the controller, the response is not waiting for the result in the controller.
So, I am not getting correct response (return value) from the service layer.
controller =>
const { serviceCreateData } = require("../services/data.servises");
module.exports.createData = async (req, res) => {
try {
const result = await serviceCreateData(req.body);
if (result) {
res.status(result.status).json({ message: result.msg });
}
} catch (error) {
res.status(500).json({ message: "Server is not responding!!" });
}
};
Service layer
/** #format */
const dataModel = require("../models/data.model");
module.exports.serviceCreateData = async (insertedData) => {
const { name, email, username, phone, address } = insertedData;
try {
dataModel.findOne({ email }).exec(async (error, user) => {
if (user) {
return { success: false, status: 400, msg: "Data already exist!!" };
} else {
const _data = new dataModel({
name,
email,
username,
phone,
address,
});
await _data.save((error, data) => {
if (error) {
return {
success: false,
status: 400,
msg: "Something went wrong!!",
};
}
if (data) {
return {
success: true,
status: 201,
body: "Data inserted successfully!!",
};
}
});
}
});
} catch (error) {
return { success: false, status: 400, msg: "Something went wrong!!" };
}
};
I need to return the value from the services layer to controller, so that I can send it to the client.
Right now I am getting error responses from the controller only.
All the help is appreciated.
So, I'm creating a login functionality using Mongoose with Node.js. Where I'm getting error while inserting wrong password.
Here is the error which I'm getting in response:
status: 0,
msgCode: 421,
message: error,
responseData: {}
I don't know why but I'm not getting proper error for Wrong Password situation which is
status: 0,
msgCode: 420,
message: 'Invalid credentials',
responseData: {}
Here is my code :
Controller :
module.exports.login = (req, res) => {
var user = {
email: req.body.email,
password: req.body.password
};
var rules = {
email: 'required|email',
password: 'required'
};
Validator(user, rules, {}, (err, status) => {
if (!status) {
res.json({
status: 0,
msgCode: 412,
message: 'Validation failed',
responseData: err
});
} else {
userModel.login(user).then(dbResponse => {
if (dbResponse.email != null) {
jwt.sign(user, key.secret, { expiresIn: 600000 }, (error, token) => {
if (error) {
res.json({
status: 0,
msgCode: 418,
message: error,
responseData: {}
});
}
else {
res.status(200).json({
status: 1,
message: 'Login successful',
responseData: { token: token, userData: dbResponse }
});
}
});
} else {
res.json({
status: 0,
msgCode: 420,
message: 'Invalid credentials',
responseData: {}
});
}
}).catch((error) => {
res.json({
status: 0,
msgCode: 421,
message: error,
responseData: {}
});
});
}
});
}
Model :
module.exports.login = (user) => {
return new Promise((resolve, reject) => {
userModel.findOne({ email: user.email }, (error, row) => {
if (row) {
if ( bcrypt.compareSync(user.password, row.password) ) {
resolve(row);
} else {
reject(error);
}
} else {
reject(error);
}
});
});
}
Please help. Thanks
As the answer from Lzok already said, because you reject the promise if the password mismatch in the userModel.login(user) function, the catch block will be always executed and the execution will never reach the res.json statement with msgCode 420
The fast solution is to modify the login function like this:
module.exports.login = (user) => {
return new Promise((resolve, reject) => {
userModel.findOne({ email: user.email }, (error, row) => {
if (row) {
if ( bcrypt.compareSync(user.password, row.password) ) {
resolve(row);
} else {
resolve({ email: null });
}
} else {
reject(error);
}
});
});
}
But it would be better to resolve with an undefined:
module.exports.login = (user) => {
return new Promise((resolve, reject) => {
userModel.findOne({ email: user.email }, (error, row) => {
if (row) {
if ( bcrypt.compareSync(user.password, row.password) ) {
resolve(row);
} else {
resolve(undefined);
}
} else {
reject(error);
}
});
});
}
And to update your if statement in the then block:
if (dbResponse !== undefined && dbResponse.email !== null) {
jwt.sign(user, key.secret, { expiresIn: 600000 }, (error, token) => {
if (error) {
That is because this line userModel.login(user).then(...).catch(...) is catching the error from the login method of your model. It does not matter which reject, both are captured in this catch.
Maybe you can use a type error there and check it in the original catch, something like:
userModel.login(user).then(...).catch((error) => {
if (error.type === 'invalid_credentials') {
res.json({
status: 0,
msgCode: 420,
message: 'Invalid credentials',
responseData: {}
});
}
// Maybe another handles you want here. Or return the default error
res.json({
status: 0,
msgCode: 421,
message: error,
responseData: {}
});
});
I'm using Mongoose 5.8.6 and I cannot receive modified model (in response is nothing) in request response. Saving is OK, but only response is empty. I added { new: true } option and still the same. Anyone knows where is the problem?
this.router.patch(this.path + '/:id', this.modifyConfiguration)
private modifyConfiguration = async (req: Request, res: Response) => {
if (!req.body || (!req.body.name && !req.body.config) || !req.params.id) {
return res.status(400).json({ message: 'Incorrect request content' });
}
await Configuration.findByIdAndUpdate(req.params.id, req.body, { new: true }, async (err, item) => {
if (err) {
return res.status(400).json({ message: err.message });
} else {
return res.status(204).json(item);
}
});
}
Your code should be like this
this.router.patch(this.path + '/:id', this.modifyConfiguration)
private modifyConfiguration = async (req: Request, res: Response) => {
try {
if (!req.body || (!req.body.name && !req.body.config) || !req.params.id) {
return res.status(400).json({ message: 'Incorrect request content' });
}
const item = await Configuration.findByIdAndUpdate(req.params.id, req.body, { new: true });
return res.status(204).json(item);
} catch (error) {
return res.status(400).json({ message: error.message });
}
}