/express-json-validator-middleware

Express.js middleware for JSON schema validation

Primary LanguageJavaScriptMIT LicenseMIT

express-json-validator-middleware

express.js middleware for validating requests against JSON Schema

Build Status codecov npm npm npm


Coming from express-jsonschema? Read our migration notes

Starting with 1.1.0, this module uses ajv@5, read their changelog and migration guide here.

Why use this library over express-jsonschema ?

  • Performance - ajv offers a significant performance boost over JSONSchema.
  • Latest JSON Schema Standard - ajv supports JSON Schema v6 proposal.
  • Active Maintenance - express-json-validator-middleware is being actively maintained.

Why validate with JSON schemas?

  • Simple - JSON schemas are a simple and expressive way to describe a data structure.
  • Standard - JSON schemas are not specific to Javascript. In fact, they are used just about everywhere.
  • Fail-Fast - Catch errors early in your logic, evading confusing errors later.
  • Separate Validation - Keep your routes clean. Validation logic doesn't need to be defined in your route handlers.
  • Error Messaging - Ajv provides you with rich error objects that can easily be transformed into human-readable format.
  • Documentation - Creating a JSON Schema is another way of documenting your application.

Installation

$ npm install express-json-validator-middleware

--save is no longer necessary as of npm@5

Getting started

  1. Install the module
  2. Require the module
var { Validator, ValidationError } = require('express-json-validator-middleware');
  1. Initialize a Validator instance, optionally passing in an ajv#options object
var validator = new Validator({allErrors: true});
  1. Optional - Define a shortcut function.
var validate = validator.validate;
  1. Use the function as an Express middleware, passing in an options object of the following format:
validate({
    request_property: schema_to_use
})

Example: Validate req.body against BodySchema

app.post('/street/', validate({body: BodySchema}), function(req, res) {
    // route code
});

Error handling

On encountering erroneous data, the validator will call next() with a ValidationError object. It is recommended to setup a general error handler for your app where you will catch ValidationError errors

Example - error thrown for the body request property

ValidationError {
    name: 'JsonSchemaValidationError',
    validationErrors: {
        body: [AjvError]
    }
}

More information on ajv#errors

Example Express app

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

var { Validator, ValidationError } = require('express-json-validator-middleware');

// Initialize a Validator instance first
var validator = new Validator({allErrors: true}); // pass in options to the Ajv instance

// Define a shortcut. It is perfectly okay to use validator.validate() as middleware, this just makes it easier
var validate = validator.validate;

// Define a JSON Schema
var StreetSchema = {
    type: 'object',
    required: ['number', 'name', 'type'],
    properties: {
        number: {
            type: 'number'
        },
        name: {
            type: 'string'
        },
        type: {
            type: 'string',
            enum: ['Street', 'Avenue', 'Boulevard']
        }
    }
}

// This route validates req.body against the StreetSchema
app.post('/street/', validate({body: StreetSchema}), function(req, res) {
    // At this point req.body has been validated and you can
    // begin to execute your application code
});

Validating multiple request properties

Sometimes your route may depend on the body and query both having a specific format. In this example we use body and query but you can choose to validate any request properties you like.

var TokenSchema = {
    type: 'object', // req.query is of type object
    required: ['token'] // req.query.token is required
    properties: {
        token: { // validate token
            type: 'string', 
            format: 'alphanumeric',
            minLength: 10,
            maxLength: 10
        }
    }
}

app.post('/street/', validate({body: StreetSchema, query: TokenSchema}), function(req, res) {
    // application code
});

Using dynamic schema

Instead of passing schema object you can also pass a function that will return a schema. It is useful if you need to generate or alter the schema based of Request object.

Example: loading schema from the database

function loadSchema(req, res, next) {
    getSchemaFromDB()
        .then((schema) => {
            req.schema = schema;
            next();
        })
        .catch((err) => next(err));
}

function getSchema(req) {
    return req.schema || DefaultSchema;
}

app.post('/street/', loadSchema, validate({body: getSchema}), function(req, res) {
    // route code
});

A valid request must now include a token URL query. Example valid URL: /street/?token=F42G5N5BGC

Custom keywords

Ajv custom keywords must be defined before any validate() middleware

Example:

var { Validator, ValidationError } = require('express-json-validator-middleware');
var validator = new Validator({allErrors: true});

validator.ajv.addKeyword('constant', { validate: function (schema, data) {
  return typeof schema == 'object' && schema !== null
          ? deepEqual(schema, data)
          : schema === data;
}, errors: false });

// route handlers with validate()

More info on custom keywords: ajv#customs-keywords

Ajv instance

The Ajv instance can be accessed via validator.ajv.

var { Validator, ValidationError } = require('express-json-validator-middleware');
var validator = new Validator({allErrors: true});

validator.ajv // ajv instance

Tests

Tests are written using Mocha & Chai

npm install
npm test

More documentation on JSON schemas

Migrating from express-jsonschema

In express-jsonschema, you could define a required property in two ways. Ajv only supports one way of doing this.

// CORRECT
{
    type: 'object',
    properties: {
        foo: {
            type: 'string'
        },
        required: ['foo'] // <-- correct way
    }
}

// WRONG
{
    type: 'object',
    properties: {
        foo: {
            type: 'string',
            required: true // nono way
        }
    }
}

Credits