Express validator is one of the many npm packages for validating a request an express application.
I recently used express validator
in a project and stumbled upon a few challenges which I'm going to share in this article.
Note: This article assumes that the reader already has an express project and only wants to implement validation using
express validator
. Hence, some details may be skipped.
When you visit the express validator docs, you'd notice the way the validator was used in the examples as shown in the snippet below:
// ...rest of the initial code omitted for simplicity.
const { check, validationResult } = require('express-validator')
app.post(
'/user',
[
// username must be an email
check('username').isEmail(),
// password must be at least 5 chars long
check('password').isLength({ min: 5 }),
],
(req, res) => {
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors.array() })
}
User.create({
username: req.body.username,
password: req.body.password,
}).then(user => res.json(user))
}
)
Looking at the snippet above, you'd notice that the validation is tightly coupled to the route definition. That pattern may be okay for a very simple use case but when usage scales, it'd be difficult for the codebase to be maintained and also it makes the route definition not readable.
In this article, I'll be showing how the validation above can be made more readable and easier to maintain.
Step 1
Create a file named validator.js
Inside the validator.js
, we are going to add two functions, one of the functions will hold the validation rules, while the second will contain the function the does the actual validation.
Copy the snippet below into the validator.js
const { body, validationResult } = require('express-validator')
const userValidationRules = () => {
return [
// username must be an email
body('username').isEmail(),
// password must be at least 5 chars long
body('password').isLength({ min: 5 }),
]
}
const validate = (req, res, next) => {
const errors = validationResult(req)
if (errors.isEmpty()) {
return next()
}
const extractedErrors = []
errors.array().map(err => extractedErrors.push({ [err.param]: err.msg }))
return res.status(422).json({
errors: extractedErrors,
})
}
module.exports = {
userValidationRules,
validate,
}
Step 2
Now re-writing the initial snippet above, we'd have:
const { userValidationRules, validate } = require('./validator.js')
app.post('/user', userValidationRules(), validate, (req, res) => {
User.create({
username: req.body.username,
password: req.body.password,
}).then(user => res.json(user))
})
Now if you try to register a user without meeting the specification for the user data, the validation error response would look like shown below:
{
"errors": [
{
"username": "username must be an email"
},
{
"password": "password must be at least 5 chars long"
},
]
}
Conclusion
With this method in place, you can define the validation rules for each route or module in a separate file as you may deem fit and then chain it with the validate middleware
. That way the code looks much cleaner, easier to read and easier to maintain.
This article has a lot of assumptions and hence some details were skipped. However, if you do have any question, feel free to reach out to me.
You can read more about express validator
on the official documentation website
This article was originally published on my blog
Latest comments (45)
Hell,Thanks For Shairng !
I want to just to know if someone use the validator.js library
Awesome article. I have recently used this implementation in my clone application with slight tweeks.
Thanks for the share! Cheers 🥂
Great solution, thanks!
Hi Chinedu,
Was looking for a way to make a custom middleware for this validator and this solved my problem brilliantly!
Awesome work
I found a more simplistic approach of doing this on Stackoverflow.
thank you!
In this function if we want to support multiple validation rules and make a condition based on one key in the request body, do you have any ideas? In the other hand how we can pass req in this function?
Thanks for sharing! Very useful.
Thanks Chinedu
userValidationRules() why do we need to use parentheses to call this function in the route. since this is a callback right ?
Love this approach to validation but wondering how I would compare the value of one field to another with it? For example password and confirmpassword. Any help would be appreciated.
You can use a custom rule
See example from the docs:
Thank you, it worked beautifully. I'm guessing you can use a similar approach to checking for 'unique' emails/usernames by requiring the model in the validator class, using the value to search for existing emails/usernames in the database and if one is returned, to throw an error?
yes
Thank you Chinedu. Just what I was looking for. Very elegant. However, this is just for one API handle. What if there are multiple request handles and each of them with multiple parameters?
Should I create a separate layer for validations for a real-world application??
Hi Chinedu,
Thank you for the solution, looks great and works great.
I am using PHPStorm/WebStorm and when I implemented this solution, the editor shows warning (yellow underline):
Argument types do not match parameters
I thought I was using correctly the pattern of adding middleware to my routes, did I do something wrong?
Thank you so much! There are so many articles which are misleading.
Great post! However I have a question. Can this be merged in a single middleware? Instead of two functions?
Certainly yes, I've recently made an improvement to it in my personal codes. The validator takes schema as a param. I found it cleaner and I'd update the article ASAP. Below is what it looks like:
How would you implement mutually exclusive properties like google maps geocoding?
can be either: address or latlng but not both
github.com/express-validator/expre...
seems oneOf is middleware not a ValidationChain[]. I'm handling it manually using a custom rule because that allows me to continue using similar "validate" middleware, although, what else are we missing by having this convenience "validate" wrapper .
Great writeup!
This is just awesome! Thank you so much!
Anytime!