express-validation a middleware that validates the body, params, query, headers of a request and returns a 400 Bad request response if the validation fails.
JavaScript

README.md

express-validation

express-validation is a middleware that validates the body, params, query, headers and cookies of a request and returns a response with errors; if any of the configured validation rules fail.

build status

Install

$ npm install express-validation --save

Supporting

express-validation supports validating the following:

  • body
  • params
  • query
  • headers
  • cookies

Setup

In order to setup and use express-validation consider the following simple express application. It has a single route; configured to use the express-validation middleware; it accepts as input validation.login; which are the validation rules we have defined for this route.

file: test/app.js

var express = require('express')
  , validate = require('express-validation')
  , http = require('http')
  , bodyParser = require('body-parser')
  , cookieParser = require('cookie-parser')
  , app = express();

app.use(bodyParser.json())
app.use(cookieParser())

app.set('port', 3000);

app.post('/login', validate(validation.login), function(req, res){
    res.json(200);
});

// error handler, required as of 0.3.0
app.use(function(err, req, res, next){
  res.status(400).json(err);
});

http.createServer(app);

The following section defines our validation rules validation.login. This is simply an object, which uses https://github.com/spumko/joi to define validation rules for a request.

We have defined two rules email and password. They are encapsulated inside body; which is important; as this defines their location, alternatives being, params, query, headers and cookies.

file: test/validation/login.js

var Joi = require('joi');

module.exports = {
  body: {
    email: Joi.string().email().required(),
    password: Joi.string().regex(/[a-zA-Z0-9]{3,30}/).required()
  }
};

The following test, calls the route defined in our express application /login; it passes in a payload with an email and empty password.

file: test/body.js

describe('when the request has a missing item in payload', function () {
  it('should return a 400 ok response and a single error', function(done){

    var login = {
        email: "andrew.keig@gmail.com",
        password: ""
    };

    request(app)
      .post('/login')
      .send(login)
      .expect(400)
      .end(function (err, res) {
        var response = JSON.parse(res.text);
        response.errors.length.should.equal(1);
        response.errors[0].messages.length.should.equal(2);
        done();
      });
    });
});

Running the above test will produce the following response.

{
  "status": 400,
  "statusText": "Bad Request",
  "errors": [
    {
      "field": "password",
      "location": "body",
      "messages": [
        "the value of password is not allowed to be empty",
        "the value of password must match the regular expression /[a-zA-Z0-9]{3,30}/"
      ],
      "types": [ "any.empty", "string.regex.base" ]
    }
  ]
}

Full code for these examples is to be found in test/ directory.

How to use

req objects gets parsed

When Joi validates the body, params, query, headers or cookies it returns it as Javascript Object.

Example without express-validation:

app.post('/login', function(req, res){
  console.log(req.body); // => '{ "email": "user@domain", "password": "pwd" }'
  res.json(200);
});

Example with express-validation:

var validate = require('express-validation');
var validation = require('./test/validation/login.js');

app.post('/login', validate(validation.login), function(req, res){
  console.log(req.body); // => { email: "user@domain", password: "pwd" }
  res.json(200);
});

The difference might seem very slight, but it's a big deal. All parts of a request will be either parsed, or throw errors.

Accessing the request context

Enabling a configurable flag, contextRequest, the Joi validation can access parts of the node http.ClientRequest. This allows you to reference other parts of the request in your validations, as follows:

Example: Validate that the ID in the request params is the same ID as in the body for the endpoint /context/:id.

file: test/validation/context.js

var Joi = require('joi');

module.exports = {
  body: {
      id: Joi.string().valid(Joi.ref('$params.id')).required()
  }
};

The following test calls the /context/1 route in the express application; It passes a payload with the an id of '2'.

file: test/context.js

  describe('when the schema contains an invalid reference to the request object', function() {
    it('should return a 400 response', function(done) {
      request(app)
        .post('/context/1')
        .send({id: '2'})
        .expect(400)
        .end(function(err, res) {
          if(err) {
            return done(err);
          }
          done();
        });
    });
  });
});

Running the above test will produce the following response:

{
  "status": 400,
  "statusText": "Bad Request",
  "errors": [
    {
      "field": "id",
      "location": "body",
      "messages": [
        "\"id\" must be one of [context:params.id]"
      ],
      "types": [
        "any.allowOnly"
      ]
    }
  ]
}

Working with headers

When creating a validation object that checks req.headers; please remember to use lowercase names; node.js will convert incoming headers to lowercase:

var Joi = require('joi');

module.exports = {
  headers: {
    accesstoken: Joi.string().required(),
    userid : Joi.string().required()
  }
};

Distinguish Error(s) from ValidationError(s)

Since 0.4.0 express-validation calls next() with a ValidationError, a specific type of Error. This can be very handy when writing more complex error handlers for your Express application, a brief example follows:

var ev = require('express-validation');

// error handler
app.use(function (err, req, res, next) {
  // specific for validation errors
  if (err instanceof ev.ValidationError) return res.status(err.status).json(err);

  // other type of errors, it *might* also be a Runtime Error
  // example handling
  if (process.env.NODE_ENV !== 'production') {
    return res.status(500).send(err.stack);
  } else {
    return res.status(500);
  }
});

Options

Unknown schema fields - strict checking

By default, additional fields outside of the schema definition will be ignored by validation.
To enforce strict checking, set the allowUnknown\* options as follows:

module.exports.post = {
  options : {
    allowUnknownBody: true,
    allowUnknownHeaders: true,
    allowUnknownQuery: true,
    allowUnknownParams: true,
    allowUnknownCookies: true
  },
  ...
};

With strict checking enabled, if additional fields gets sent through validation, they will be raise a ValidationError.

Specific Status codes and text

By default, the status code is set to 400, and status text to Bad Request, you can change this behaviour with the following:

module.exports.post = {
  options: {
    status: 422,
    statusText: 'Unprocessable Entity'
  },
  ...
};

Global options

Status code and text can also be customized globally. At the same time specific behaviour still applies.

var ev = require('express-validation');
// assign options
ev.options({
  status: 422,
  statusText: 'Unprocessable Entity'
});

// clear options back to default
ev.options();

Thanks to node require() caching, all the other express-validation instances also have the same set of global options.

Full options list

Recap of all options usable both as global or per-validation basis.

allowUnknownBody: boolean - default: true
allowUnknownHeaders: boolean - default: true
allowUnknownQuery: boolean - default: true
allowUnknownParams: boolean - default: true
allowUnknownCookies: boolean - default: true
status: integer - default: 400
statusText: string - default: 'Bad Request'
contextRequest: boolean - default: false

Changelog

Moved to CHANGELOG.md

License

This work is licensed under the MIT License (see the LICENSE file).

https://github.com/AndrewKeig/express-validation/blob/master/LICENSE

Contributors