Express-validator not breaking on right chain - node.js

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

Related

Why do the property and the value disappear from the real database?

I try to console.log the user as below showing for the further manipulation of it but the weird thing is the places properties somehow disappeared, and if you check on the database, it's still there.
Anyone, please tell me what's wrong ...
const { validationResult } = require('express-validator');
const HttpError = require('../models/http-error');
const getCoordsForAddress = require('../util/location');
const PlaceSchema = require('../models/place_schema');
const UserSchema = require('../models/user_schema');
const { default: mongoose } = require('mongoose');
const { findById } = require('../models/place_schema');
const getPlaceById = async (req, res, next) => {
const placeId = req.params.pid;
let place;
try {
place = await PlaceSchema.findById(placeId);
} catch (err) {
const error = new HttpError('Somthing wehnt wrong, could not find a place', 500);
return next(error);
};
if (!place) {
const error = new HttpError('Could not find a place for the provided id.', 404);
return next(error);
} else {
res.json({ place: place.toObject({ getters: true }) });
};
};
const getPlacesByUserId = async (req, res, next) => {
const userId = req.params.uid;
let places;
try {
places = await PlaceSchema.find({ creator: userId });
} catch (err) {
const error = new HttpError('Something went wrong, couldn nott find the place', 500);
return next(error);
};
if (!places || places.length === 0) {
return next(
new HttpError('Could not find a place for the provided user id.', 404)
);
};
res.json({ places: places.map(p => p.toObject({ getters: true })) });
};
const createPlace = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log(errors);
return next(new HttpError('Invalid inputs passed, please check your data', 422));
};
const { title, description, address, creator, image } = req.body;
let coordinates;
try {
coordinates = await getCoordsForAddress(address);
} catch (error) {
return next(error);
}
const createdPlace = new PlaceSchema({
title,
description,
image,
location: coordinates,
address,
creator
});
// check if a exist userid is existing
let user;
try {
user = await UserSchema.findById(creator);
} catch (err) {
const error = new HttpError('Creating place failed, please try again.', 500)
return next(error);
};
if (!user) {
const error = new HttpError('Could not find user for provided id', 404)
return next(error);
}
try {
const sess = await mongoose.startSession();
sess.startTransaction();
await createdPlace.save({ session: sess });
user.places.push(createdPlace);
await user.save({ session: sess });
await sess.commitTransaction();
} catch (err) {
const error = new HttpError('Creating place failed, please try again.', 500);
return next(error);
};
res.status(201).json({ place: createdPlace });
};
const updatePlace = async (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log(errors);
return next(HttpError('Invalid inputs passed, please check your data', 422));
};
const { title, description } = req.body;
const placeId = req.params.pid;
try {
PlaceSchema.findByIdAndUpdate(placeId, {
title, description
})
} catch (err) {
const error = new HttpError('Somthing went wrong, could not update the place.', 500)
return next(error);
};
res.status(200).json({ place: updatedPlace.toObject({ getters: true }) });
};
const deletePlace = async (req, res, next) => {
const placeId = req.params.pid;
let thePlace;
let creator;
try {
thePlace = await PlaceSchema.findById(placeId);
creator = await UserSchema.findById(thePlace.creator);
} catch (err) {
const error = new HttpError('Somthing went wrong, could delete the place', 500);
return next(error);
};
console.log(creator);
try {
const sess = await mongoose.startSession();
sess.startTransaction();
await PlaceSchema.findByIdAndDelete(placeId, { session: sess });
await creator.places.pull(creator);
await UserSchema.save({ session: sess });
await sess.commitTransaction();
} catch (err) {
// console.log(err)
const error = new HttpError('Somthing went wrong, could delete the place', 500);
return next(error);
};
res.status(200).json({ message: 'Deleted place.' });
};
exports.getPlaceById = getPlaceById;
exports.getPlacesByUserId = getPlacesByUserId;
exports.createPlace = createPlace;
exports.updatePlace = updatePlace;
exports.deletePlace = deletePlace;
When console.log the user, there is no places of an array showing
But it's actually in the database, somehow it disappears when console.log

Joi Validation Sequelize Custom Method

Im currently trying to validate if the username already exists through Sequelize however currently the promise is returned AFTER the response is sent back to the client meaning Joi thinks the username doesnt exist yet every time. Anyone know how to not have Joi just finish validation prematurely and actually process the query/return the right result.
schema.js
const Joi = require('joi')
const { User } = require('../models')
const validateUsername = (value, helpers) => {
User.findOne({ where: { username: value } }).then(user => {
if (user) {
return helpers.error('username.exists')
}
return value
})
}
const schema = Joi.object({
body: {
username: Joi.string().min(3).required().custom(validateUsername)
.messages({
'string.base': 'Invalid Username',
'string.empty': 'Username Required',
'string.min': 'Username Must Be At Least {#limit} Characters Long',
'any.required': 'Username Required',
'username.exists': 'Username Already In Use'
})
}
})
module.exports = schema
validator.js
const options = {
abortEarly: false,
allowUnknown: true,
stripUnknown: true
}
const validator = (schema) => {
return (req, res, next) => {
const { error } = schema.validate(req, options)
const valid = error == null
if (valid) {
next()
} else {
const { details } = error;
const errors = details.map(i => i.message)
res.status(422).json({ errors })
}
}
}
module.exports = validator

express-validator isDate and isISO8601 are always false

I'm trying to validate a date. I have tried everything I can but I have not found a solution. Input {"dob": "2002-10-02"}
'use strict'
var validator = require('validator');
var controller = {
create: (req,res) =>{
//pick parameters
var parameters = req.body;
//validator
try {
//not working (always returns false)
//var validate_dob = validator.isDate(parameters.dob + '');
//also not working (always returns false)
//var validate_dob = validator.isISO8601(parameters.dob + '');
} catch (error) {
return res.status(400).send({
message: error
});
}
}
}
In your question you mention tag express-validator, but in your middleware you use pure validator.
Here I am putting an example using the express-validator lib (version 6.6.0). To use validate body parameters (login and password). But you can get the idea and pick the validation for your date from the validators list. Reference.
server/validators/login.validator.js
const { body, validationResult } = require('express-validator');
exports.validationBodyRules = [
body('login', 'login is required').exists(),
body('password', 'password is required').exists(),
body('login', 'login is required').notEmpty(),
body('password', 'password is required').notEmpty()
];
exports.checkRules = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
};
Here is the routes file
server/routes/login.route.js
const router = require('express').Router();
const loginService = require('../controllers/login.controller');
const loginValidator = require('../validators/login.validator');
router.post('/login', loginValidator.validationBodyRules, loginValidator.checkRules, loginService.hashPassword, loginService.lookupLogin, loginService.logEmployee);
module.exports = router;
server/controllers/login.controller.js
const postgres = require('../../lib/postgres');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
exports.logEmployee = (req, res) => {
res.status(200).json({ token: 'Bearer ' + jwt.sign(req.employee, process.env.SECRET, { expiresIn: 1800 }) });//expires in 1800 seconds
res.end();
};
exports.hashPassword = (req, res, next) => {
crypto.scrypt(req.body.password.toString(), 'salt', 256, (err, derivedKey) => {
if (err) {
return res.status(500).json({ errors: [{ location: req.path, msg: 'Could not do login', param: req.params.id }] });
}
req.body.kdfResult = derivedKey.toString('hex');
next();
});
};
exports.lookupLogin = (req, res, next) => {
const sql = 'SELECT e.employee_id, e.login FROM employee e WHERE e.login=$1 AND e.password = $2';
postgres.query(sql, [req.body.login, req.body.kdfResult], (err, result) => {
if (err) {
return res.status(500).json({ errors: [{ location: req.path, msg: 'Could not do login', param: req.params.id }] });
}
if (result.rows.length === 0) {
return res.status(404).json({ errors: [{ location: req.path, msg: 'User or password does not match', param: req.params.id }] });
}
req.employee = result.rows[0];
next();
});
};
But you can use the ideas here to use a date validator.
If you need a more complete example, please, let me know.

How do I Solve Express Validator Middleware Error?

First, I have userController like this:
File userController.js
const { validationResults } = require('express-validator');
const { userSignUpValidation } = require('../validator/userSignUpValidate');
function userCreation(req, res) {
try{
const errors = validationResults(req);
if(errors) {
return res.status(400)
.json({
error: {
message: errors.array()[0].msg
}
})
}
bcrypt.hash(req.body.userPassword, saltRounds, function(err, hash) {
User.create({
userId: req.body.userId,
userEmail: req.body.userEmail,
userPhoneNumber: req.body.userPhoneNumber,
userPassword: hash
})
.then((user) => {
return res.status(200).json(user);
})
.catch((error) => {
return res.status(400).json.error;
});
});
} catch(error) {
return res.status(400)
.json({
error: {
message: error
}
})
}
}
In the validator/userSignUpValidate.js the code like this:
'use strict'
const { check } = require('express-validator');
module.exports = [
check('userId').isLength({ min: 5 }).withMessage('The username at least more than 5 characters!'),
check('userPassword').isLength({ min: 6 }).withMessage('The password at least more than 6 characters!'),
check('userPhoneNumber').isLength({ min: 10 }).withMessage('The phone number at least more than 10 characters!'),
check('userEmail').isEmail().withMessage('Is your email correct? Please be correct!')
]
When I test it on postman, the json response always show error by catch on userController.js without error message.
{
"error": {
"message": {}
}
}
My question. To ensure the express-validator run in the right place, where should I put the code?
Firstly you need to import validationResult from express-validator, in your code you are importing validationResults.
Secondly, you are not using your userSignUpValidate middleware. This middleware can be used in controller but it is better to appy it in the userRoute to keep controller clean.
So let's apply userSignUpValidate middleware to the userRoutes.
If the file paths don't match yours, please fix them.
const express = require("express");
const router = express.Router();
const usersController = require("../controllers/userController");
const userSignUpValidate = require("../validator/userSignUpValidate");
router.post("/register", [userSignUpValidate], usersController.userCreation);
module.exports = router;
Then in the controller, we need to import validationResult from express-validator and use it:
const { validationResult } = require("express-validator");
function userCreation(req, res) {
try {
const errors = validationResult(req);
if(!errors.isEmpty()) {
console.log(errors);
return res.status(400).json({
error: {
message: errors.array()[0].msg
}
});
}
//other code you have
} catch (error) {
return res.status(400).json({
error: {
message: error
}
});
}
}
exports.userCreation = userCreation;
When we send a request body with a 3 character password like this:
{
"userId": "userid",
"userPassword": "123",
"userPhoneNumber": "1234567890",
"userEmail": "abc#gmail.com"
}
The response will be like this with a 400 - Bad Request:
{
"error": {
"message": "The password at least more than 6 characters!"
}
}
you should pass like this
const errors = validationResults(req.body);
const Joi = require('#hapi/joi');
const loginValidation = data =>{ const schema = {
username: Joi.string().min(5).required(),
password: Joi.string().min(5).required(),
deviceId: Joi.string().min(2).required()
};
return Joi.validate(data, schema); };
module.exports.loginValidation = loginValidation;
My Routes File
const { registerValidation, loginValidation } = require('../../validation'); router.post('/login', async (req, res)=>{
try {
//validate the data to be submitted
const { error } = loginValidation(req.body);
if (error) return res.status(200).send(
{
status: 0,
message: "Validarion Error",
details: error.details[0].message
});
} catch (error) {
res.status(400).send({
status: 0,
message: "Failed",
error: error
});
}});
this code works fine and is in production mode
Just try like below,
const express = require('express');
const { check, oneOf, validationResult } = require('express-validator');
const app = express();
//this function you need to export in controller
const xxx = (req, res, next) => {
try {
validationResult(req).throw();
res.status(200).send('success');
} catch (err) {
console.log('error messagecame')
res.status(422).json(err);
}
}
app.get('/', oneOf([
check('lang').isIn(['js', 'react', 'angular'])
]), xxx);
app.listen(4000, ()=>{
console.log("running server 4k")
})
Sample working copy
UPDATE 2
I hope below one will help full,
const express = require('express');
const { check, oneOf, validationResult } = require('express-validator');
const app = express();
const xxx = [
check('lang').isIn(['js', 'react', 'angular']),
(req, res, next) => {
try {
validationResult(req).throw();
res.status(200).send('success');
} catch (err) {
console.log('error messagecame')
res.status(422).json(err);
}
}
]
app.get('/', xxx);
app.listen(4000, ()=>{
console.log("running server 4k")
})
SAMPLE WORKING COPY2
Error
https://TruthfulWeirdIrc--five-nine.repl.co?lang=jssddf
Success
https://TruthfulWeirdIrc--five-nine.repl.co?lang=js

How to validate multipart/form-data in Express-validator using Nodejs

A snippet of my code where i show my defined router,checking body param and checking for validation error.
My defined Post request:
router.post("/addEmployee",upload.any(), function(req, res, next) {
/*I used multer because body data from multipart/form-data*/
var input = JSON.parse(req.body.data);
Server validation: // validation not work because req.checkBody only get bodyData now i am using multipart/form-data (req.body.data)
req.checkBody('EMPID', "**EMPID must be Integer**").notEmpty().isNumeric();
req.checkBody('PAYROLLID', "**PAYROLLID must be Integer**").notEmpty().isNumeric();
.....
....
Check validation error
var validationError = req.validationErrors(); //check error
// if (validationError) { //suppose get error -- throw that error
res.json(validationError[0].msg);
} else { //validation success
......strong text
Use express validator middleware after the multer that's why express validator work naturally
This example work for me , try this
import require dependencies
var fs = require('fs');
var path = require('path');
var multer = require('multer');
var mkdirp = require('mkdirp');
configure multer
var obj = {};
var diskStorage = multer.diskStorage({
destination:function(req,file,cb){
var dest = path.join(__dirname,'/uploads');
mkdirp(dest,function(err){
if(err) cb(err,dest);
else cb(null,dest);
});
},
filename:function(req,file,cb){
var ext = path.extname(file.originalname);
var file_name = Date.now()+ext;
obj.file_name = file_name;
cb(null,file_name);
}
});
var upload = multer({storage:diskStorage});
prepare validation middleware
var validator = function(req,res,next){
req.checkBody('name').notEmpty().withMessage('Name field is required');
req.checkBody('email')
.notEmpty()
.withMessage('Email field is required')
.isEmail()
.withMessage('Enter a valid email address')
.isEmailExists()
.withMessage('Email address already exists');
req.checkBody('password').notEmpty().withMessage('Password field is required');
req.checkBody('retype_password').notEmpty().withMessage('Retyp password field is required');
req.checkBody('password').isEqual(req.body.retype_password).withMessage('Password and confirm password did not match.');
req.checkBody('address').notEmpty().withMessage('Address field is required');
req.asyncValidationErrors().then(function() {
next();
}).catch(function(errors) {
req.flash('errors',errors);
res.status(500).redirect('back');
});
}
save requested data into db
router.post('/store',upload.single('avatar'),validator,async function(req,res,next){
var newUser = User({
name:req.body.name,
email:req.body.email,
password:req.body.password,
avatar:obj.file_name,
address:req.body.address,
created_at:new Date()
});
try{
await newUser.save();
req.flash('success_message','New user has been successfully created');
return res.redirect('back');
}catch(err){
throw new Error(err);
}
});
Guys I find one solution
router.post("/addEmployee",upload.any(), function(req, res, next) {
/*I used multer because body data from multipart/form-data*/
var input = JSON.parse(req.body.data);
req.body = input;// solution this line
});
This works for me on the route part of desired endpoint
var express = require("express");
var router = express.Router();
const grant = require("../controllers/grant");
const { body, check } = require("express-validator");
const {
authenticate,
checkIfAdmin,
checkIfSuper,
checkIfApplicant,
} = require("../middlewares/authentication");
const { validateGrant } = require("../middlewares/validator");
const { uploadHandler } = require("../middlewares/call");
const {
multerUpload,
cloudinaryConfig,
paystack,
} = require("../config/config");
uploadHandler = (req, res, next) => {
req.upload(req, res, (err) => {
if (err) {
res.status(400)
.json({ message: `Bad request upload handler, ${err.message}`, success: false })
.end();
} else {
// special workaround for files validating with express-validator
req.body.files = req.files;
next();
// console.log("req.files", req.upload, "here", req.files, "body", req.body.files);
}
});
};
router.post(
"/create",
authenticate,
checkIfSuper,
function (req, res, next) {
req.upload = multerUpload.fields([
{ name: "main", maxCount: 1 },
{ name: "banner", maxCount: 1 },
]);
next();
},
uploadHandler,
[
check("name").not().isEmpty().withMessage("Grant Name is required"),
check("description")
.isString()
.withMessage("Grant Description is required"),
check("sdgGoals")
.not()
.isEmpty()
.withMessage("Grant Sdg Goals are required"),
check("deadline")
.not()
.isEmpty()
.withMessage("Grant Application Deadline is required"),
check("available")
.isBoolean()
.withMessage("Grant Availability is required"),
check("about").isString().withMessage("Grant About is required"),
check("by").isString().withMessage("Grant Creator is required"),
],
cloudinaryConfig,
grant.create
);
grant.create is
const { error } = require("../middlewares/response");
const { grants } = require("../models/grant");
const { validationResult } = require("express-validator");
const Datauri = require("datauri/parser");
const path = require("path");
const { uploader } = require("../config/config");
exports.create = async function (req, res, next) {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: errors.array()[0].msg,
});
}
if (!req.files) {
return res.status(400).json({
success: false,
message: "No files were uploaded",
});
}
const dUri = new Datauri();
const dataUri = (file) =>
dUri.format(path.extname(file.originalname).toString(), file.buffer);
const {
title,
description,
sdgGoals,
applicationDeadline,
applicationLimit,
available,
} = req.body;
let promises = [];
let imgs = [];
// console.log("req.files", req.files, "here", req.body.files);
for (const key in req.files) {
if (Object.hasOwnProperty.call(req.files, key)) {
const f = req.files[key][0];
console.log(f, "before uri")
//console.log(dataUri(f)); -> //fileName: '.png', mimetype: 'image/png', and content
const file = dataUri(f).content;
try {
const resp = await uploader.upload(file);
// console.log("resp url and image", resp.url, image);
imgs.push({ name: key, url: resp.url });
} catch (err) {
return next(err);
}
}
}
console.log("imgs", imgs);
const grant = new Grant({
...req.body,
createdBy: req.user._id,
images: imgs,
});
const result = await grant.save();
return res.status(201).json({
message: "Grant created successfully",
data: {
grant: result,
},
});
} catch (err) {
next(err);
}
};
exports.create = function (req, res) {
req.assert('name', 'Name cannot be blank').notEmpty();
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password cannot be blank').notEmpty();
req.assert('password', 'Password must be at least 8 chars long').isLength({ min: 8 });
const errors = req.validationErrors();
if (errors) {
return res.status(200).send(errors);
}
req.body.password = bcrypt.hashSync(req.body.password, 10);
var model = new Admin(req.body);
model.save(function (err, admin) {
if (err) {
res.status(500).send({ error: err });
return;
} else {
res.status(200).send(admin);
}
});
};

Resources