Today I want
to discuss Server-side validation techniques in NodeJS.
we need to install a package called express
validator
npm install --save express validator
in routes
const { check } = require("express-validator");
router.post("/signup", check("email").isEmail(), authController.postSignup);
/*
here we can check about
incoming fields in new middleware -> check
also we can add multiple
checks like password validation, jwt validation etc
look below ->
multiple validators
*/
in the controller we can get the validation outputs. (in next middleware -> postSignup)
const { validationResult } = require("express-validator");
exports.postSignup = (req, res, next) => {
const email = req.body.email;
const password = req.body.password;
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log(errors.array());
/*
errors printed as an array
error msg=>errors.array()[0].msg => outputs Invalid value <- we can customize this
*/
//return
res.status(422).json({ errors: errors.array() }); for REST
return res.status(422).render("auth/signup", {
//for
templating engines
active: "reset",
pageTitle: "Password Reset",
errorMessage: "",
});
}
}
Customize massage
in router
router.post(
"/signup",
check("email").isEmail().withMessage("Please Enter a valid Email..!!!"),
authController.postSignup
);
now that console log
returns the error as => Please Enter a valid Email...!!! (instead of Invalid Value)
Custom validators
router.post(
"/signup",
check("email", "Please Enter a
valid Email..!!!") //set the default err msg here
.isEmail()
.custom((value, { req }) => {
if (value === "test@gmail.com") {
throw new Error("THis Email is forbidden"); //we can improve this by adding blacklist in our database and
cheaking forbidden once
}
return true;
}),
authController.postSignup
);
Multiple Validators
const { check, body } = require("express-validator");
//we can check many things
like- > cookies , headers, query params etc
To add
multiple check middleware’s -> wrap
them inside an array
router.post(
"/signup",
[check("email"),check("password") ] //likewise
,
authController.postSignup
);
Example
router.post(
"/signup",
[
check("email", "Please Enter Valid
email")
.isEmail()
.custom((value, { req }) => {
if (value === "test@gmail.com") {
throw new Error("THis Email is
forbidden");
}
return true;
}),
body("password", "Plaese Enter Valid
Password")
.isLength({ min: 5 })
.isAlphanumeric(),
],
authController.postSignup
);
Checking for Field Equality
router.post(
"/signup",
[
check("email", "Please Enter Valid
email")
.isEmail()
.custom((value, { req }) => {
if (value === "test@gmail.com") {
throw new Error("THis Email is
forbidden");
}
return true;
}),
body("password", "Plaese Enter Valid
Password")
.isLength({ min: 5 })
.isAlphanumeric(),
---------------------new-------------------------------
body("confirmPassword").custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error("Password Not Matching");
}
return true;
}),
],
authController.postSignup
);
Async validation ->
Reject
where a user is found to that email.
const User = require("../models/user");
router.post(
"/signup",
[
check("email", "Please Enter Valid
email")
.isEmail()
.custom((value, { req }) => {
return User.findOne({ email: value }).then((userdoc) => {
if (userdoc) {
return Promise.reject("Email already
Exists");
}
});
}),
body("password", "Plaese Enter Valid
Password")
.isLength({ min: 5 })
.isAlphanumeric(),
body("confirmPassword").custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error("Password Not Matching");
}
return true;
}),
],
authController.postSignup
);
keeping user Input After validation
We can send the old
request email and password with the new request.
return res.status(422).render("auth/signup", {
active: "reset",
pageTitle: "Password Reset",
errorMessage: errors.array()[0].msg,
oldInputs:{email: req.body.email}
//previously entered
email you can send like this
});
how to add responsive errors to relevant fields
we can use the param value inside the error
message object
we can pass the full error array to the
response
return res.status(422).render("auth/signup", {
active: "reset",
pageTitle: "Password Reset",
oldInputs:{email: req.body.email},
validationErrors:errors.array() //we pass all the errors
});
Then we can get and examine the param
field (by iterating one by one)
<input type="text" name="email" id="email" class=<%= validationErrors.find(e=>e.param==='email') ?'invalidClz':''%>>
This finds
matching email field and if founds one=> it will assign a class called
invalidClz.
so now in
the css we can add the class
.invalidClz{
border-color:red
}
This can
repeat to other inputs also
Sanitizing data
We can
normalize the inputs -> convert to lowercase, trim etc
.normalizeEmail()
.trim()
Also there is a security feature called XSS
(cross Site Scripting) sanitization. I’ll focus on visual sanitization.
router.post(
"/signup",
[
check("email") //remove the default
msg-> u can pass it withMessage
.isEmail()
.normalizeEmail() //convert into lowecase
.custom((value, { req }) => {
return User.findOne({ email: value }).then((userdoc) => {
if (userdoc) {
return Promise.reject("Email already
Exists");
}
return true;
});
}),
body("password", "Plaese Enter Valid
Password")
.isLength({ min: 5 })
.isAlphanumeric()
.trim(), //remove white sapaces
body("confirmPassword").custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error("Password Not Matching");
}
return true;
}),
],
authController.postSignup
);

0 Comments