I want to validate the request object using Express-Validator. Let's assume I have two routes, a GET /users/:id (fetchUserById) and POST /users (createUser) route
this.router = express.Router();
this.router.route('/').post(this.userRequestValidator.createUser, this.userController.createUser);
this.router.route('/:id').get(this.userRequestValidator.fetchUserById, this.userController.fetchUserById);
As you can see I call the validation middleware right before calling the controller logic. First I created a base validator dealing with the validation errors and returning a HTTP 400 if something failed.
export abstract class RequestValidator {
protected validate = async (request: Request, response: Response, next: NextFunction): Promise<void> => {
const errors: Result<ValidationError> = validationResult(request);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
next();
}
};
}
My validator functions userRequestValidator.createUser and userRequestValidator.fetchUserById just have to extend the RequestValidator and implement the validations
export class UserRequestValidator extends RequestValidator {
public createUser = [
body('username')
.isString()
.exists(),
body('password')
.isString()
.exists(),
this.validate,
];
public fetchUserById = [
param('id')
.isString()
.isUUID()
.exists(),
this.validate,
];
}
When I call GET localhost:3000/users/abc
I get this response
{
"errors": [
{
"value": "abc",
"msg": "Invalid value",
"param": "id",
"location": "params"
}
]
}
This is the response I am expecting. But when I call POST localhost:3000/users
with an empty body I get this response
{
"errors": [
{
"msg": "Invalid value",
"param": "username",
"location": "body"
},
{
"msg": "Invalid value",
"param": "username",
"location": "body"
},
{
"msg": "Invalid value",
"param": "password",
"location": "body"
},
{
"msg": "Invalid value",
"param": "password",
"location": "body"
}
]
}
Does someone know how I can fix this behaviour or what's wrong with my setup?
I want to validate the request object using Express-Validator. Let's assume I have two routes, a GET /users/:id (fetchUserById) and POST /users (createUser) route
this.router = express.Router();
this.router.route('/').post(this.userRequestValidator.createUser, this.userController.createUser);
this.router.route('/:id').get(this.userRequestValidator.fetchUserById, this.userController.fetchUserById);
As you can see I call the validation middleware right before calling the controller logic. First I created a base validator dealing with the validation errors and returning a HTTP 400 if something failed.
export abstract class RequestValidator {
protected validate = async (request: Request, response: Response, next: NextFunction): Promise<void> => {
const errors: Result<ValidationError> = validationResult(request);
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() });
} else {
next();
}
};
}
My validator functions userRequestValidator.createUser and userRequestValidator.fetchUserById just have to extend the RequestValidator and implement the validations
export class UserRequestValidator extends RequestValidator {
public createUser = [
body('username')
.isString()
.exists(),
body('password')
.isString()
.exists(),
this.validate,
];
public fetchUserById = [
param('id')
.isString()
.isUUID()
.exists(),
this.validate,
];
}
When I call GET localhost:3000/users/abc
I get this response
{
"errors": [
{
"value": "abc",
"msg": "Invalid value",
"param": "id",
"location": "params"
}
]
}
This is the response I am expecting. But when I call POST localhost:3000/users
with an empty body I get this response
{
"errors": [
{
"msg": "Invalid value",
"param": "username",
"location": "body"
},
{
"msg": "Invalid value",
"param": "username",
"location": "body"
},
{
"msg": "Invalid value",
"param": "password",
"location": "body"
},
{
"msg": "Invalid value",
"param": "password",
"location": "body"
}
]
}
Does someone know how I can fix this behaviour or what's wrong with my setup?
Share Improve this question edited Sep 23, 2019 at 19:53 Question3r asked Sep 23, 2019 at 19:47 Question3rQuestion3r 3,80229 gold badges122 silver badges240 bronze badges 1- which version of express-validator are you using? – MrfksIV Commented Sep 26, 2019 at 6:22
2 Answers
Reset to default 13 +50I don't know why when req.body
is a empty object - {}
, the validator will run through all node of validation chain. You can check again, add each message for each condition, like as follow:
class UserRequestValidator extends RequestValidator {
public createUser = [
body('username')
.isString().withMessage('username must be a string') // you can see both error messages in the response
.exists().withMessage('username must be exist'),
body('password') // the same for this field
.isString()
.exists(),
this.validate,
];
public fetchUserById = [
param('id') // because id is exist in `req.params`, then only one test has been executed.
.isString().withMessage('id must be a string')
.isUUID()
.exists(),
this.validate,
];
}
I found a solution for your case in https://github./express-validator/express-validator/issues/638 , stop chain in the first error with .bail()
function.
Then your validator class will be like:
class UserRequestValidator extends RequestValidator {
public createUser = [
body('username')
// always check exists() first
.exists().withMessage('username must be exist').bail()
.isString().withMessage('username must be a string').bail(),
body('password')
.exists().bail()
.isString().bail(),
this.validate,
];
public fetchUserById = [
param('id')
.isString()
.isUUID()
.exists(),
this.validate,
];
}
You can also set onlyFirstError
to true
when retrieving the error array.
From the documentation:
If the option onlyFirstError is set to true, then only the first error for each field will be included
Example usage:
function validateRequestParams (req, res, next) {
const errors = validationResult(req)
if (errors.isEmpty()) {
return next()
} else {
return res.status(400).json({
bodyValidationErrors: errors.array({ onlyFirstError: true })
})
}
}