Skip to content

Latest commit

 

History

History
249 lines (173 loc) · 11.1 KB

input-validation-in-express-with-express-validator.md

File metadata and controls

249 lines (173 loc) · 11.1 KB

Welcome to this short tutorial in which you'll learn how to validate form input in an Express app using an open-source Node module called express-validator.

Installation

Installing express-validator is easy when you use npm. Just input the following command:

npm install express-validator

This will download the latest version of the express-validator npm package.

Once the command has done executing - if you were to to look inside the "node modules" folder, you'd see that express-validator has a transitive dependency on another module called validator:

You'll see why this is important later.

Setup

For the purposes of this tutorial, I'll assume you have a script whose contents look something like this:

// dependencies
var express = require('express');
var bodyParser = require('body-parser');

var app = express();

// middleware
app.use(bodyParser.urlencoded({ extended: false }));

(If you don't, you can quickly create a application skeleton using the application generator tool.)

express-validator is middleware which you can mount by calling app.use:

// dependencies
var express = require('express');
var bodyParser = require('body-parser');
var validator = require('express-validator');

var app = express();

// middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(validator());

There is only one thing you need to watch out for and that is, you must use express-validator after you use the body parser middleware. If you don't, express-validator will not work. This is because without the body parser middleware, it would not be possible to parse the values to validate.

Validation

In order to illustrate validation in action, we'll imagine that we have a request handler:

app.post('/pages/create', function(req, res) {
});

And that the request body looks like this:

{
  leader_email: "leader@something.edu",
  leader_mobile_no: "017171",
  team_twitter: "http://twitter.com/teamA",
  member_count: 10,
  page_color: "#FFFFFF",
  page_accent_colour: "#FFFFFF"
}

Here we want to validate that...

  • leader_email is a valid email address*
  • leader_mobile_no is a valid UK mobile phone number
  • team_twitter is a valid Twitter handle
  • member_count is both a number and divisble by 2
  • page_color and page_accent_color are valid hex color codes

*Remember. Validation is not the same as verification. Validation merely confirms that the email is in the correct format. Verification confirms that that the user has access to the email. If you need to verify the email, consider sending a verification email.

Validating an email is a common requirement so we might as well start there.

Inside of the request handler, make a call to the req.checkBody function. Pass to this function the name of the input you want to validate (in this case, "leader_email") and the error message you want to return if the input is invalid:

app.post('/pages/create', function(req, res) {
    req.checkBody("leader_email", "Enter a valid email address.");
});

Next, tell the middleware which validators to apply by chaining one or more validation functions:

app.post('/pages/create', function(req, res) {
  req.checkBody("leader_email", "Enter a valid email address.").isEmail();
});

Hopefully now you can see why express-validator depending on validator is not merely an implementation detail. You almost always end up calling functions from the validator library of validation functions. You can see a full list of validation functions here.

You might be wondering where exactly the checkBody function comes from. It comes from the express-parser middleware. Express middleware can add any property it wants to the req argument. In addition to the checkBody function, express-validator adds two more functions namely, checkQuery and checkParams which can be used to validate query parameters and route parameters respectively. See the documentation for more information on those.

For completeness, here is how I would validate the remaining input elements:

req.checkBody("leader_email", "Enter a valid email address.").isEmail();

req.checkBody(
  "leader_mobile_no",
  "Enter a valid UK phone number.").isMobilePhone("en-GB");

req.checkBody(
  "team_twitter",
   "Enter a valid Twitter URL").optional().matches("http://twitter.com/*");

req.checkBody(
  "contestant_count",
  "Contestant count must be a number and one that is divisible by 2"
).isNumber().isDivisibleBy(2);

req.checkBody(
  "page_color",
  "Page colour must be a valid hex color"
).isHexColor();

req.checkBody(
  "page_color_accent",
  "Page colour accent must be a valid hex color").isHexColor();

Hopefully by now the above listing is intuitive. If you are unsure about what any of the functions do, you can refer to their descriptions here. Aside from that, there are just a couple of things to note about this code:

  • In a couple of places we chain more than one validation function. This is possible thanks to fluent interfaces.
  • For the "team twitter" input we chain the optional function before chaining the matches function. The optional functions says: "this value is optional so do not attempt to validate it with matches unless the user actually entered something". In other words, if the "team twitter" input is empty, matches won't be called and the input will be deemed valid.

Remember. All we've done so far is define some rules. We are yet to enforce these rules. We'll do that now.

In order to determine whether the input is valid according to the rules, we call a function called validationErrors. If any input is invalid, this function returns an array of errors; otherwise - if all input is valid - it returns undefined (which indicates "no errors"). If the function returns an array of errors, we'll show them to the user:

app.post('/pages/create', function(req, res) {
  req.checkBody("leader_email", "Enter a valid email address.").isEmail();

  var errors = req.validationErrors();
  if (errors) {
    res.send(errors);
    return;
  } else {
    // normal processing here
  }
});

(As you may have noticed, I removed most validation rules from the request handler - this is solely for the sake of brevity.)

Whilst emitting errors in JSON format might be appropriate for a web service, for most websites, this is not very nice. In such a case we can send errors back with a template:

app.post('/pages/create', function(req, res) {
  req.checkBody("leader_email", "Enter a valid email address.").isEmail();

  var errors = req.validationErrors();
  if (errors) {
    res.render('create', { errors: errors });
    return;
  } else {
    // normal processing here
  }
});

And then in the template, show the errors conditionally:

if errors  
  ul
    for error in errors
      li!= error.msg

This is an example using the Jade template engine. I know not everyone digs Jade so for good measure, here is another example, this time using the Handlebars template engine:

{{ #if errors }}
  <ul>
    {{ #each errors }}
      <li>{{ this.msg }}</li>
    {{ /each }}
  </ul>
{{ /if }}

Extending express-validator

Sometimes your validation rules will be too complex to convey using the built-in validation functions alone. Fortunately, express-validator allows you to define custom validation functions.

To illustrate custom validation functions, imagine that the user is asked to enter exactly two, unique, comma-delimited values.

Valid inputs would include:

  • wibble, wobble
  • wubble, flob

Invalid inputs would include:

  • wibble, wibble
  • wibble
  • wibble, wobble, wubble

Although it might be possible to create a regular expression for this rule, I speculate that doing so would come at too much of a cost to readability. So, let us define a custom validation function.

Go back to the script in which you mounted the express-validator middleware and replace...

app.use(validator());

With...

app.use(validator({
  customValidators: {
    containsTwoTags: function (input) {
      var tags = input.split(',');
      // Remove empty tags
      tags = tags
        .filter(function(tag) { return /\S/.test(tag) });
      // Remove duplicate tags
      tags = tags
        .filter(function(item, pos, self) {
          return self.indexOf(item) == pos;
        });
      return tags.length <= 2;
    }
  }
})

In the above listing we pass an object literal with a customValidators property to the middleware. On this customValidtors property we define a function: containsTwoTags. I will not belabor the details of this function because they are too specific to this example. What I will explain is that the function accepts a input argument and returns true if the input is valid; otherwise, it returns false. When you define your own validation function, you'll need to ensure that it does the same.

Once you have defined your custom validator, you can chain it just like you would any other validation function:

req.checkBody('tags', 'Enter exactly two distinct tags.').containsTwoTags();

In this example I just defined a single validator - you could theoretically define as many as you want on the customValidators object, though.

Conclusion

This has been an introduction to input validation in Express with express-validator. Whilst I hope I have covered enough for you to hit the ground running in your application, if you encounter any problems, please consult the documentation or get in touch and I'll be happy to help :).

P.S. If you read this far, you might want to follow me on Twitter and GitHub, or subscribe to my blog.