Express Validator, one function that will check multiple forms - node.js

I have been attempting to use Express Validator, however I have come across a problem.
Currently, I am using one route with a variable (which is the action, e.g update name, email, password etc.) to send the updated user data to the server.
I am using a switch statement that looks at the action, and does the relevant update to the user data.
I'd like one validation function that also has a switch statement, which will determine what it needs to validate.
Currently I have this so far...
this validation rules:
const validationRules = () => {
console.log("validating...");
return [
// names must be 1 or more
body("firstName").isLength({ min: 1 }),
body("lastName").isLength({ min: 1 }),
];
};
the post request:
app.post(
"/api/user/update/:action",
validationRules(),
validate,
(req, res) => {
const { id } = req.body;
const { action } = req.params;
const updateDatabase = (id, updateObject) => {
UserModel.findOneAndUpdate(
{ _id: id },
{ $set: updateObject },
{
useFindAndModify: false,
}
)
.then((user) => {
console.log(user);
res.sendStatus(200);
})
.catch((err) => {
console.log(err);
res.status(403).send({ error: err });
});
};
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
let update = {};
switch (action) {
case "name":
const { firstName, lastName } = req.body;
update = {
"userInfo.firstName": firstName,
"userInfo.lastName": lastName,
};
break;
case "email":
const { email } = req.body;
console.log(email);
update = {
"userInfo.email": email,
};
break;
case "mobile":
const { mobile } = req.body;
update = {
"userInfo.mobile": mobile,
};
break;
default:
console.log("nothing matched so nothing updated");
break;
}
updateDatabase(id, update);
}
}
);

You can make a simple middlware and the switch case there:
Here is an example:
const app = require('express')();
const validatorMiddleware = (req, res, next)=>{
switch(req.params.action){
case 'foo':
//here you will `return` the rules
console.log('foo');
break;
case 'bar':
//here you will `return` the rules
console.log('bar');
break;
default: console.log("default");
}
next();
}
app.get('/:action', validatorMiddleware, (req, res)=>{
res.send(req.params);
})
app.listen(8080, ()=>{
console.log('started');
})
For your case it will be something like this:
const validationRules = (req, res, next) => {
console.log("validating...");
switch (req.params.action) {
case "name":
return [
// names must be 1 or more
body("firstName").isLength({ min: 1 }),
body("lastName").isLength({ min: 1 }),
];
}
next();
};

Related

"Unauthorized" when attempting to send email with SendGrid in node.js

Not sure what's going on but every time I try to send an email i'm met with a error message. I've literally tried everything I can think of and Nothing. This is the message i'm receiving from postman when I hit the localhost:4000/api/send-mail route. I've been trying to work through this for hours now, if anyone can help that would be amazing. Thanks in advance!!!
{
"message": "Unauthorized",
"code": 401,
"response": {
"headers": {
"server": "nginx",
"date": "Mon, 21 Mar 2022 01:57:04 GMT",
"content-type": "application/json",
"content-length": "116",
"connection": "close",
"access-control-allow-origin": "https://sendgrid.api-docs.io",
"access-control-allow-methods": "POST",
"access-control-allow-headers": "Authorization, Content-Type, On-behalf-of, x-sg-elas-acl",
"access-control-max-age": "600",
"x-no-cors-reason": "https://sendgrid.com/docs/Classroom/Basics/API/cors.html",
"strict-transport-security": "max-age=600; includeSubDomains"
},
"body": {
"errors": [
{
"message": "The provided authorization grant is invalid, expired, or revoked",
"field": null,
"help": null
}
]
}
}
}
my user controllers code
const expressAsyncHandler = require("express-async-handler");
const sgMail = require("#sendgrid/mail");
const generateToken = require("../../config/token/generateToken");
const User = require("../../model/user/User");
const validateMongodbId = require("../../utils/validateMongodbID");
sgMail.setApiKey(process.env.SEND_GRID_API_KEY);
//-------------------------------------
//Register
//-------------------------------------
const userRegisterCtrl = expressAsyncHandler(async (req, res) => {
//Check if user Exist
const userExists = await User.findOne({ email: req?.body?.email });
if (userExists) throw new Error("User already exists");
try {
//Register user
const user = await User.create({
firstName: req?.body?.firstName,
lastName: req?.body?.lastName,
email: req?.body?.email,
password: req?.body?.password,
});
res.json(user);
} catch (error) {
res.json(error);
}
});
//-------------------------------
//Login user
//-------------------------------
const loginUserCtrl = expressAsyncHandler(async (req, res) => {
const { email, password } = req.body;
//check if user exists
const userFound = await User.findOne({ email });
//Check if password is match
if (userFound && (await userFound.isPasswordMatched(password))) {
res.json({
_id: userFound?._id,
firstName: userFound?.firstName,
lastName: userFound?.lastName,
email: userFound?.email,
profilePhoto: userFound?.profilePhoto,
isAdmin: userFound?.isAdmin,
token: generateToken(userFound?._id),
});
} else {
res.status(401);
throw new Error("Invalid Login Credentials");
}
});
//------------------------------
//Users
//-------------------------------
const fetchUsersCtrl = expressAsyncHandler(async (req, res) => {
console.log(req.headers);
try {
const users = await User.find({});
res.json(users);
} catch (error) {
res.json(error);
}
});
//------------------------------
//Delete user
//------------------------------
const deleteUsersCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
//check if user id is valid
validateMongodbId(id);
try {
const deletedUser = await User.findByIdAndDelete(id);
res.json(deletedUser);
} catch (error) {
res.json(error);
}
});
//----------------
//user details
//----------------
const fetchUserDetailsCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
//check if user id is valid
validateMongodbId(id);
try {
const user = await User.findById(id);
res.json(user);
} catch (error) {
res.json(error);
}
});
//------------------------------
//User profile
//------------------------------
const userProfileCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
try {
const myProfile = await User.findById(id);
res.json(myProfile);
} catch (error) {
res.json(error);
}
});
//------------------------------
//Update profile
//------------------------------
const updateUserCtrl = expressAsyncHandler(async (req, res) => {
const { _id } = req?.user;
validateMongodbId(_id);
const user = await User.findByIdAndUpdate(
_id,
{
firstName: req?.body?.firstName,
lastName: req?.body?.lastName,
email: req?.body?.email,
bio: req?.body?.bio,
},
{
new: true,
runValidators: true,
}
);
res.json(user);
});
//------------------------------
//Update password
//------------------------------
const updateUserPasswordCtrl = expressAsyncHandler(async (req, res) => {
//destructure the login user
const { _id } = req.user;
const { password } = req.body;
validateMongodbId(_id);
//Find the user by _id
const user = await User.findById(_id);
if (password) {
user.password = password;
const updatedUser = await user.save();
res.json(updatedUser);
} else {
res.json(user);
}
});
//------------------------------
//following
//------------------------------
const followingUserCtrl = expressAsyncHandler(async (req, res) => {
//1.Find the user you want to follow and update it's followers field
//2. Update the login user following field
const { followId } = req.body;
const loginUserId = req.user.id;
//find the target user and check if the login Id exists
const targetUser = await User.findById(followId);
const alreadyFollowing = targetUser?.followers?.find(
(user) => user?.toString() === loginUserId.toString()
);
if (alreadyFollowing) throw new Error("You are already folliwig this user");
//1. Find the user you want to follow and update it's followers field
await User.findByIdAndUpdate(
followId,
{
$push: { followers: loginUserId },
},
{ new: true }
);
//2. Update the login user following field
await User.findByIdAndUpdate(
loginUserId,
{
$push: { following: followId },
isFollowing: true,
},
{ new: true }
);
res.json("You are now following this user");
});
//------------------------------
//unfollow
//------------------------------
const unfollowUserCtrl = expressAsyncHandler(async (req, res) => {
const { unFollowId } = req.body;
const loginUserId = req.user.id;
await User.findByIdAndUpdate(
unFollowId,
{
$pull: { followers: loginUserId },
isFollowing: false,
},
{ new: true }
);
await User.findByIdAndUpdate(
loginUserId,
{
$pull: { following: unFollowId },
},
{ new: true }
);
res.json("You have successfully unfollowed this user");
});
//------------------------------
//Block Users
//------------------------------
const blockUserCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
const user = await User.findByIdAndUpdate(
id,
{
isBlocked: true,
},
{ new: true }
);
res.json(user);
});
//------------------------------
//Block user
//------------------------------
const unBlockUserCtrl = expressAsyncHandler(async (req, res) => {
const { id } = req.params;
validateMongodbId(id);
const user = await User.findByIdAndUpdate(
id,
{
isBlocked: false,
},
{ new: true }
);
res.json(user);
});
//------------------------------
// Account Verification - Send email
//------------------------------
const generateVerificationTokenCtrl = expressAsyncHandler(async (req, res) => {
try {
//build your message
const msg = {
to: "MizTamaraAndrea#gmail.com",
from: "Tamara18_1985#msn.com",
subject: "My first Node js email sending",
text: "I hope this goes through",
};
await sgMail.send(msg);
res.json("Email sent");
} catch (error) {
res.json(error);
}
});
module.exports = {
generateVerificationTokenCtrl,
userRegisterCtrl,
loginUserCtrl,
fetchUsersCtrl,
deleteUsersCtrl,
fetchUserDetailsCtrl,
userProfileCtrl,
updateUserCtrl,
updateUserPasswordCtrl,
followingUserCtrl,
unfollowUserCtrl,
blockUserCtrl,
unBlockUserCtrl,
};
My user Routes Code
const express = require("express");
const {
userRegisterCtrl,
loginUserCtrl,
fetchUsersCtrl,
deleteUsersCtrl,
fetchUserDetailsCtrl,
userProfileCtrl,
updateUserCtrl,
updateUserPasswordCtrl,
followingUserCtrl,
unfollowUserCtrl,
blockUserCtrl,
unBlockUserCtrl,
generateVerificationTokenCtrl,
} = require("../../controllers/users/usersCtrl");
const authMiddleware = require("../../middleware/auth/authMiddleware");
const userRoutes = express.Router();
userRoutes.post("/register", userRegisterCtrl);
userRoutes.post("/login", loginUserCtrl);
userRoutes.post("/send-mail", generateVerificationTokenCtrl);
userRoutes.get("/", authMiddleware, fetchUsersCtrl);
userRoutes.put("/password", authMiddleware, updateUserPasswordCtrl);
userRoutes.put("/follow", authMiddleware, followingUserCtrl);
userRoutes.put("/unfollow", authMiddleware, unfollowUserCtrl);
userRoutes.put("/block-user/:id", authMiddleware, blockUserCtrl);
userRoutes.put("/unblock-user/:id", authMiddleware, unBlockUserCtrl);
userRoutes.get("/profile/:id", authMiddleware, userProfileCtrl);
userRoutes.put("/:id", authMiddleware, updateUserCtrl);
userRoutes.delete("/:id", deleteUsersCtrl);
userRoutes.get("/:id", fetchUserDetailsCtrl);
module.exports = userRoutes;
You have one of two problems here. Either, your API key does not have the permission to send messages, or has been deleted. Create yourself a new API key that has permission to send emails and try again.
Or, I see you appear to be trying to send from an msn.com email address. In order to send from a third party domain like that, you need to have set up single sender verification. This lets SendGrid know that you do have access to that email address and that you are not trying to spoof someone else's.
(Note that when you ship to production, we recommend you use your own domain and authenticate that domain with SendGrid, which will give you much better deliverability.)

Express-validator not breaking on right chain

I am using express-validator in nodejs. The code is always throwing first error message as a validation result.
validation.js
const { check, validationResult } = require('express-validator')
const {handleError, ErrorHandler} = require('../helper/error');
const resultsOfValidation = (req,res,next) => {
const messages = [];
const errors = validationResult(req);
if(errors.isEmpty()) {
return next(); //pass to controller
}
errors.array().map( err => messages.push(err.msg));
throw new ErrorHandler(400,messages);
}
const createUserValidator = () => {
return [
check('firstName')
.exists({ checkFalsy: true }).withMessage('First Name is mandatory')
.bail()
.isAlpha().withMessage('First Name should have all alphabets')
.bail()
.isLength({min:3}).withMessage('First Name should have minimum 3 characters')
,
check('lastName')
.optional({ checkFalsy: true }) //ignore validation when null or empty
.isAlpha()
.bail()
]
}
module.exports = {
resultsOfValidation,
createUserValidator,
}
user route is below :
const router = express.Router();
router
.get('/',getAllUsers)
.post('/',createUserValidator(),resultsOfValidation,(req,res) => console.log('created'))
.get('/:id',getUserById)
.delete('/',deleteUserById)
module.exports = router;
My user.js controller file is:
module.exports = {
getAllUsers: async (req,res)=> {},
createUser: async (req,res,next)=> {
try{
const errors = resultsOfValidation(req);
if (errors.length >0) {
throw new ErrorHandler(400, errors)
}
const { firstName, lastName } = req.body;
// call createUser from model
const user = await UserModel.createUser(firstName, lastName);
if(!user) {
throw new ErrorHandler(404, 'User not created')
}
// return res.status(200).json({ success: true, user });
} catch (error) {
return res.status(500).json({ success: false, error: error })
}
},
getUserById: async (req,res)=> {},
deleteUserById: async (req,res)=> {},
}
Here is example of post request I made. It should throw First Name should have minimum 3 characters. But I am getting other error First Name is mandatory

User details not getting updated in nodejs

I am trying to get the functionality of user updating his profile while being logged in.But when i try to update the fields,for example the "name" field,it doesn't get updated although it runs without any error.It remains the same even after running properly.
Here is my code.
const filterObj = (obj, ...allowedFields) => {
const newObj = [];
Object.keys(obj).forEach((el) => {
if (allowedFields.includes(el)) newObj[el] = obj[el];
});
return newObj;
};
exports.updateMe = catchAsync(async (req, res, next) => {
if (req.body.password || req.body.passwordConfirm) {
return next(new AppError('This route is not for password updates.', 400));
}
//Filtering out field names that are not allowed to be updated
const filteredBody = filterObj(req.body, 'name', 'email');
//Updating the user data
const updatedUser = await User.findByIdAndUpdate(req.user.id, filteredBody, {
new: true,
runValidators: true,
});
res.status(200).json({
status: 'success',
data: {
user: updatedUser,
},
});
});

How to prevent dublicated items in mongoDB when an item is updated

I am creating a multi-user dictionary, where several users can add words. I don't want that the same word could have dublicates. I was able to prevent dublicates when a word is added, but struggle with a functionality to prevent dublicates when a word is updated. For example, there is a word "apple" in a database, let's imagine someone wanted to update a word "apply" and accidentally wrote "apple", in such a case, updated word "apple" should not go to database, since there is already such word there. Please help me.
Words API
const express = require('express');
const router = express.Router();
const Word = require('../../models/Word');
const validateWordInput = require('../../validation/word');
const passport = require('passport');
// #route GET api/words/test
// #desc tests words route
// #access Public
router.get('/test', (req, res) => res.json({ msg: 'Words works' }));
// #route POST api/words
// #desc Add words to profile
// #access Private
router.post(
'/',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Word.find({}).then(word => {
if (
word.filter(
wrd =>
wrd.ugrWordCyr.toString().toLowerCase() ===
req.body.ugrWordCyr.toLowerCase()
).length !== 0
) {
return res
.status(404)
.json({ wordalreadyexists: 'Word already exists' });
} else {
const newWord = new Word({
user: req.user.id,
ugrWordCyr: req.body.ugrWordCyr,
rusTranslation: req.body.rusTranslation,
example: req.body.example,
exampleTranslation: req.body.exampleTranslation,
origin: req.body.origin,
sphere: req.body.sphere,
lexis: req.body.lexis,
grammar: req.body.grammar,
partOfSpeech: req.body.partOfSpeech,
style: req.body.style
});
newWord.save().then(word => res.json(word));
}
});
}
);
// #route Put api/words/:id
// #desc Update a word by id
// #access Private
router.put(
'/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Profile.findOne({ user: req.user.id }).then(profile => {
Word.findById(req.params.id)
.then(word => {
// Check for word owner
if (word.user.toString() !== req.user.id) {
return res
.status(401)
.json({ notauthorized: 'User not authorized' });
}
const wordID = req.params.id;
const wordInput = req.body;
// Update
Word.findByIdAndUpdate(
{ _id: wordID },
{ $set: wordInput },
{ returnOriginal: false },
(err, word) => {
if (err) {
console.log(err);
}
}
).then(word => {
res.json(word);
});
})
.catch(err => res.status(404).json({ nowordfound: 'No word found' }));
});
}
);
// #route GET api/words
// #desc Dislay all words
// #access Public
router.get('/', (req, res) => {
Word.find()
.sort({ date: -1 })
.then(words => res.json(words))
.catch(err => res.status(404).json({ nonwordsfound: 'No words found' }));
});
//#route Get api/words/:id
//#desc Get word by id
//#access Public
router.get('/:id', (req, res) => {
Word.findById(req.params.id)
.then(word => res.json(word))
.catch(err =>
res.status(404).json({ nonwordfound: 'No word found with that ID' })
);
});
//#route DELETE api/words/:id
//#desc DELETE word
//#access Private
router.delete(
'/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
Profile.findOne({ user: req.user.id }).then(profile => {
Word.findById(req.params.id)
.then(word => {
// Check for post owner
if (word.user.toString() !== req.user.id) {
return res
.status(401)
.json({ notauthorized: 'User not authorized' });
}
// Delete
word.remove().then(() => res.json({ success: true }));
})
.catch(err => res.status(404).json({ postnotfound: 'No post found' }));
});
}
);
module.exports = router;
I used the following code to prevent dublicated on addition.
if (
word.filter(
wrd =>
wrd.ugrWordCyr.toString().toLowerCase() ===
req.body.ugrWordCyr.toLowerCase()
).length !== 0
) {
return res
.status(404)
.json({ wordalreadyexists: 'Word already exists' });
} else {
const newWord = new Word({
user: req.user.id,
ugrWordCyr: req.body.ugrWordCyr,
rusTranslation: req.body.rusTranslation,
example: req.body.example,
exampleTranslation: req.body.exampleTranslation,
origin: req.body.origin,
sphere: req.body.sphere,
lexis: req.body.lexis,
grammar: req.body.grammar,
partOfSpeech: req.body.partOfSpeech,
style: req.body.style
});
What code should I write to do the same on update?
Before executing Word.findByIdAndUpdate(), you can do a check to see if the text in req.body matches any existing words in your database.
// #route Put api/words/:id
// #desc Update a word by id
// #access Private
router.put(
'/:id',
passport.authenticate('jwt', { session: false }),
(req, res) => {
const { errors, isValid } = validateWordInput(req.body);
// Check validation
if (!isValid) {
// Return any errors
return res.status(400).json(errors);
}
Profile.findOne({ user: req.user.id }).then(profile => {
Word.findById(req.params.id)
.then(word => {
// Check for word owner
if (word.user.toString() !== req.user.id) {
return res
.status(401)
.json({ notauthorized: 'User not authorized' });
}
const wordID = req.params.id;
const wordInput = req.body;
//find all words
Word.find()
.then((allWords) => {
//create an array of strings using each word ("apple", "apricot", ...etc)
const wordStrings = allWords.map((word) => word.ugrWordCyr) //not sure if thats the property that has the word-spelling
//check if user input already exists in all words
if(wordStrings.includes(req.body.ugrWordCyr)){
return res.status(400).json({ error : "word already exists" })
}
// Update
Word.findByIdAndUpdate(
{ _id: wordID },
{ $set: wordInput },
{ returnOriginal: false },
(err, word) => {
if (err) {
console.log(err);
}
}).then(word => {
res.json(word);
});
})
.catch((errors) => {
return res.status(400).json({ errors: "could not find any words" })
})
})
.catch(err => res.status(404).json({ nowordfound: 'No word found' }));
});
}
);
Alternatively, you could also update your Word model and use the unique property when setting up your mongoose schema. I imagine your schema looks something like this:
const mongoose = require("mongoose")
const wordSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
ugrWordCyr: { type: String, unique: true}, <-- set unique to true
rusTranslation: { type: String },
example: { type: String },
exampleTranslation: { type: String },
origin: { type: String },
sphere: { type: String },
lexis: { type: String },
grammar: { type: String },
partOfSpeech: { type: String },
style: { type: String }
})
const Word = mongoose.model("Word", userSchema)
module.exports = Word
Now when you use findByIdAndUpdate(), it will not complete unless you pass in a new/unique string to ugrWordCyr or whatever you use as the explicit word.
Word.findByIdAndUpdate(
{ _id: wordID },
{ $set: wordInput },
{ returnOriginal: false },
(err, word) => {
if (err) {
console.log(err);
}
}
).then(word => {
res.json(word);
});
.catch(err => {
return res.status(400).json({ error: "could not update word" })
})
})

Cannot set headers after they are sent to the client - Entries saved to MongoDb - Express.Js

I know this question gets asked a lot but I cannot tell where I am sending multiple headers. The data is getting stored in the database and then it crashes. I am fairly new to Node/Express and I think I might be missing something fundamental here.
I have tried reading what I could find on stackoverflow and figured out the reason I am getting this error is because it is sending multiple header requests. Tried updating the code with little tweaks but nothing has worked so far.
Thanks for the help.
Dashboard Controller -
exports.getGymOwnerMembersAdd = (req, res, next) => {
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
const oldInput = {
...
};
Membership
.find()
.then(memberships => {
res.render('gym-owner/members-add', {
memberships: memberships,
oldInput: oldInput,
errorMessage: message,
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
validationErrors: []
});
})
.catch(err => {
console.log(err);
});
}
exports.postGymOwnerMembersAdd = (req, res, next) => {
const membershipId = req.body.membershipLevel;
const errors = validationResult(req);
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
if(!errors.isEmpty()) {
Membership
.find()
.then(memberships => {
return res.status(422).render('gym-owner/members-add', {
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
});
})
.catch(next);
}
bcrypt
.hash(password, 12)
.then(hashedPassword => {
const user = new User({
...
});
return user.save();
})
.then(result => {
res.redirect('/gym-owner-dashboard/members');
})
.catch(err=> {
console.log(err);
});
}
Dashboard Routes And Validation-
router.get('/gym-owner-dashboard/members-add', isAuth, isGymOwner, dashboardController.getGymOwnerMembersAdd);
router.post(
'/gym-owner-dashboard/members-add',
isAuth, isGymOwner,
[
check('name')
.isAlpha().withMessage('Names can only contain letters.')
.isLength({ min: 2 }).withMessage('Please enter a valid name')
.trim(),
check('email')
.isEmail().withMessage('Please enter a valid email.')
.custom((value, { req }) => {
return User.findOne({
email: value
}).then(userDoc => {
console.log('Made it here!');
if(userDoc) {
return Promise.reject('E-mail already exists, please pick a different one.');
};
});
})
.normalizeEmail(),
...
check(
'password',
'Please enter a password at least 5 characters.'
)
.isLength({ min: 5 })
.trim(),
check('confirmPassword')
.trim()
.custom((value, { req }) => {
if(value !== req.body.password) {
throw new Error('Passwords have to match!');
}
return true;
})
],
dashboardController.postGymOwnerMembersAdd
);
Expected Results
Create a new user while passing validation.
Actual Results
A new user is created and saved to Mongodb. The user gets redirected back to the user creation page with an error that the user is undefined. The server crashes with the error "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
I understand you have a bug in "postGymOwnerMembersAdd".
if(!errors.isEmpty()) {
Membership
.find()
.then(memberships => {
return res.status(422).render('gym-owner/members-add', { // this return refers to cb but not to middleware
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
});
})
.catch(next);
}
bcrypt
.hash(password, 12)
.then(hashedPassword => {
const user = new User({
...
});
return user.save();
})
.then(result => {
res.redirect('/gym-owner-dashboard/members');
})
.catch(err=> {
console.log(err);
});
Thus, both the "return res.status(422).render()" and the "res.redirect('/gym-owner-dashboard/members')" will be executed, and this trigger error (header set after they are sent).
I mean two solutions to the problem
First: use async/await
exports.postGymOwnerMembersAdd = async (req, res, next) => {
const membershipId = req.body.membershipLevel;
const errors = validationResult(req);
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
if(!errors.isEmpty()) {
try {
const memberships = await Membership.find();
return res.status(422).render('gym-owner/members-add', {
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
};
} catch (err) {
next(err);
}
}
const hashedPassword = await bcrypt.hash(password, 12);
const user = new User({
...
});
await user.save();
return res.redirect('/gym-owner-dashboard/members');
};
Second: use else
exports.postGymOwnerMembersAdd = (req, res, next) => {
const membershipId = req.body.membershipLevel;
const errors = validationResult(req);
let message = req.flash('error');
if(message.length > 0) {
message = message[0];
} else {
message = null;
}
if(!errors.isEmpty()) {
Membership
.find()
.then(memberships => {
return res.status(422).render('gym-owner/members-add', {
pageTitle: 'Add Members',
path: '/gym-owner-dashboard/members-add',
errorMessage: errors.array()[0].msg,
message: message,
memberships: memberships,
oldInput: {
...
},
validationErrors: errors.array()
});
})
.catch(next);
} else {
bcrypt
.hash(password, 12)
.then(hashedPassword => {
const user = new User({
...
});
return user.save();
})
.then(result => {
res.redirect('/gym-owner-dashboard/members');
})
.catch(err=> {
console.log(err);
});
}
}

Resources