A Clean Approach to Using Express Validator

Chinedu Orie - Aug 9 '19 - - Dev Community

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))
  }
)
Enter fullscreen mode Exit fullscreen mode

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,
}
Enter fullscreen mode Exit fullscreen mode

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))
})
Enter fullscreen mode Exit fullscreen mode

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"
        },
    ]
}
Enter fullscreen mode Exit fullscreen mode

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

. . . . . . . . . . . . . . . . . . . . .