Validation in Express with express-validator

Kiptoo Korir
5 min readNov 26, 2021
Upclose photo of javascript code typed out in a code editor
Photo by Gabriel Heinzer on Unsplash

Data validation is the process of checking the accuracy and quality of source data against a set of predefined rules and constraints before processing it.

Whether its in securing an application or ensuring that the validity of data is maintained, validation often goes a long way in the prevention of what could be major problems.

This tutorial will look at how to leverage some of the functionality that the express-validator package offers to check data provided by a request before its passed on to the controller.

We will be using CommonJs modules rather than ES6 modules.

Project Setup

We will setup a new project by creating an empty folder and initializing the app from the command line.

mkdir validation-tutorial && cd validation-tutorial

Once the directory has been created and directory has been changed into the new folder, we will initialize the app and create the package.json file using:

npm init

With the package.json file created, we can add our dependencies using the commands:

npm i express express-validator --save
npm i -D nodemon

The above commands will install the express and express-validator packages and add nodemon as a dev dependency.

Server Configuration

We will make a minor addition to the package.json file to setup nodemon to run from an npm script command

{
"name": "validation-tutorial",
"version": "1.0.0",
"description": "A project demonstrating how to utilize express-validator",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon ./server.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"nodemon": "^2.0.15"
},
"dependencies": {
"express": "^4.17.1",
"express-validator": "^6.13.0"
}
}

With the addition made, the server can be run using the command

npm run dev

The initializing command out of the way, we can look at working on the server.js file which will be the entry point to the application.

const app = require("../app")();
const http = require("http");
http.createServer(app).listen(app.get("port"), function () {
console.log("Express Server Runing on port " + app.get("port"));
});

The app.js will handle our the app configuration

const express = require("express");
const { router: usersRouter } = require("./routes/usersRouter");
module.exports = () => {
const app = express();
app.set("port", 3000);
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/api/users", usersRouter);
return app;
};

We are going to have our routes in a separate routers file. With the above setup, our folder structure should look like so

App Folder Structure

Understanding express-validator

express-validator is essentially a combination of express.js middlewares and as such its methods are utilized as middlewares in express routers. However, these methods/middleware are built on top of the validators and sanitizers provided by validator.js.

As such some methods in use may not be available in express-validator’s docs. These methods can be found here in the documentation of validator.js.

First Approach: Handling Validation In Router

We will first look at the users routes file, more specifically the create-user route.

As shown above, we will use the check method from express-router. check() allows for use of multiple validators and on the same input parameter. The input can be from any of the request objects available such as the body, cookies, headers or params.

The validators chained to the check method each perform a specific action and add on more behavior to the chain, for instance:

  • trim() removes the white space around the request parameter and
  • escape() performs HTML escaping and prevents markup from being passed to the controller
  • notEmpty() ensures that the request parameter has some content
  • withMessage() allows for the use of a custom error message should the validator preceeding it result in a rejection of the request parameter
  • isEmail() prevents the use of an invalid email

Some validators take in some form of input, for instance isStrongPassword takes in an object with the rules that define how a strong password should be gauged. The structure of the object can be found here.

Custom Validators

We can also use custom validators by using the custom() which takes in a callback as a parameter.

Within the callback function, the first parameter is the request parameter that is being validated.

Assuming we had a data access method titled getByEmail which checks for records in the users table based on the email, we could run the query and if the result was not empty, we could reject the input with a given message by returning Promise.reject in the callback. This is a common use case when registering new users.

Within custom validators, the express request object can also be accessed as shown in the custom validator in use within the check for the confirmPassword input.

In the callback passed to the custom method, we can access the req object through destructuring the second parameter. We have renamed the req to request and as shown we can access the password input in the line:

request.body.password

As shown by the two instances above, in a custom validator method, we can indicate the status of the validation in two ways:

  • By using a Promise , in which case we must use Promise.reject for failure in the validation, as indicated in the custom email check.
  • Or using the boolean values, as indicated in the check handling the equality of password and confirmPassword.

Additional Methods

The validators and sanitizers used above are certainly not exhaustive. They are many more scenarios which the above example does not cover. Additional validators, sanitizers and methods can be uncovered in the documentation of the packages mentioned, express-validator and validator.js.

Second Approach: Move Validation To Separate File

The above demonstration can suffice in an application, however if the router file contained a couple more routes, we would end up with a huge file which would be hard to traverse and time consuming.

We can move the validation logic to a separate file which would make our code more readable and easier to deal with.

We have moved all our validation logic to the usersValidator file. The first function handles the validation rules and returns an array with all of the validator methods chained together, similar to what we had in the routes file.

The second function validationHandler acts as a middleware and handles the logic for returning the response in case of an error. The validation handler can essentially be re used, for the handling of other validation instances.

The export returns an array combining the two functions.

Our routes can now be cleaner and more readable as shown below

Conclusion

Validation is an important exercise and this tutorial is an attempt at easing the friction when going about it.

The final code of the tutorial can be found here.

--

--

Kiptoo Korir

Web Developer | Information Geek | Occasional Poet - I talk about software development and life in general.