express-json-validator-middleware
Advanced tools
Comparing version 2.1.1 to 2.2.0
@@ -0,0 +0,0 @@ { |
{ | ||
"name": "express-json-validator-middleware", | ||
"version": "2.1.1", | ||
"version": "2.2.0", | ||
"description": "An Express middleware to validate requests against JSON Schemas", | ||
"main": "src/index.js", | ||
"author": "Josef Vacek", | ||
"author": "Simon Plenderleith", | ||
"license": "MIT", | ||
@@ -17,10 +17,10 @@ "keywords": [ | ||
"type": "git", | ||
"url": "git+https://github.com/JouzaLoL/express-json-validator-middleware.git" | ||
"url": "git+https://github.com/simonplend/express-json-validator-middleware.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/JouzaLoL/express-json-validator-middleware/issues" | ||
"url": "https://github.com/simonplend/express-json-validator-middleware/issues" | ||
}, | ||
"homepage": "https://github.com/JouzaLoL/express-json-validator-middleware#readme", | ||
"homepage": "https://github.com/simonplend/express-json-validator-middleware#readme", | ||
"scripts": { | ||
"test": "mocha", | ||
"test": "tap", | ||
"lint": "eslint \"src/*.js\" --fix", | ||
@@ -35,5 +35,3 @@ "prepush": "npm run lint && npm run test", | ||
"devDependencies": { | ||
"body-parser": "^1.18.3", | ||
"chai": "^3.5.0", | ||
"chai-http": "^4.2.1", | ||
"codecov": "^3.8.1", | ||
"eslint": "^6.8.0", | ||
@@ -45,10 +43,11 @@ "eslint-config-google": "^0.9.1", | ||
"mocha": "^7.1.1", | ||
"nyc": "^15.0.0", | ||
"prettier": "^1.19.1" | ||
"prettier": "^1.19.1", | ||
"simple-get": "^4.0.0", | ||
"tap": "^15.0.1" | ||
}, | ||
"dependencies": { | ||
"ajv": "^6.6.2", | ||
"@types/express": "^4.17.3", | ||
"@types/express-serve-static-core": "^4.17.2", | ||
"@types/json-schema": "^7.0.4", | ||
"@types/express-serve-static-core": "^4.17.2" | ||
"ajv": "^6.6.2" | ||
}, | ||
@@ -55,0 +54,0 @@ "husky": { |
405
README.md
@@ -1,30 +0,16 @@ | ||
# express-json-validator-middleware | ||
[express.js](https://github.com/visionmedia/express) middleware for validating requests against JSON Schema | ||
# Express JSON Validator Middleware | ||
<a href="https://www.patreon.com/bePatron?u=19773095" data-patreon-widget-type="become-patron-button"><img src="patreon.png"></img></a> | ||
[![Build Status](https://travis-ci.org/JouzaLoL/express-json-validator-middleware.svg?branch=master)](https://travis-ci.org/JouzaLoL/express-json-validator-middleware) | ||
[![codecov](https://codecov.io/gh/JouzaLoL/express-json-validator-middleware/branch/master/graph/badge.svg)](https://codecov.io/gh/JouzaLoL/express-json-validator-middleware) | ||
[![npm](https://img.shields.io/npm/dm/express-json-validator-middleware.svg)](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[![npm](https://img.shields.io/npm/v/express-json-validator-middleware.svg)](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[![npm](https://img.shields.io/npm/l/express-json-validator-middleware.svg)](https://www.npmjs.com/package/express-json-validator-middleware) | ||
> [Express](https://github.com/expressjs/express/) middleware for validating | ||
requests against JSON schemas. | ||
<hr> | ||
[![npm version](https://img.shields.io/npm/v/express-json-validator-middleware.svg)](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[![npm monthly downloads](https://img.shields.io/npm/dm/express-json-validator-middleware.svg)](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[![npm license](https://img.shields.io/npm/l/express-json-validator-middleware.svg)](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[![Build status](https://github.com/vacekj/express-json-validator-middleware/workflows/Node.js%20CI/badge.svg)](https://github.com/vacekj/express-json-validator-middleware/actions?query=workflow%3A%22Node.js+CI%22) | ||
[![codecov](https://codecov.io/gh/vacekj/express-json-validator-middleware/branch/master/graph/badge.svg)](https://codecov.io/gh/vacekj/express-json-validator-middleware) | ||
Coming from `express-jsonschema`? Read our [migration notes](#migrating) | ||
Major version `1.x` of this module uses `ajv@5`, read their changelog and migration guide [here](https://github.com/epoberezkin/ajv/releases/tag/5.0.0). | ||
Major version `2.x` uses `ajv@6` in order to support draft-07 of JSON Schema. | ||
Please keep in mind that you have to manually configure ajv to support **draft-06** schema files from now on (see https://github.com/epoberezkin/ajv#using-version-6). | ||
## Why use this library over [express-jsonschema](https://github.com/trainiac/express-jsonschema) ? | ||
- **Performance** - [ajv](https://github.com/epoberezkin/ajv) offers a [significant performance boost over](https://github.com/ebdrup/json-schema-benchmark/blob/master/README.md#performance) JSONSchema. | ||
- **Latest JSON Schema Standard** - [ajv](https://github.com/epoberezkin/ajv) supports JSON Schema v7 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. | ||
- **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. | ||
@@ -35,53 +21,87 @@ - **Separate Validation** - Keep your routes clean. Validation logic doesn't need to be defined in your route handlers. | ||
## Installation | ||
## Install | ||
```sh | ||
$ npm install express-json-validator-middleware | ||
npm install express-json-validator-middleware | ||
``` | ||
`--save` is no longer necessary as of `npm@5` | ||
## Getting started | ||
1. Require the module | ||
```js | ||
var { Validator, ValidationError } = require('express-json-validator-middleware'); | ||
``` | ||
```javascript | ||
import { Validator } from "express-json-validator-middleware"; | ||
2. Initialize a Validator instance, optionally passing in an [ajv#options](https://github.com/epoberezkin/ajv#options) object | ||
/** | ||
* Define a JSON schema. | ||
*/ | ||
const addressSchema = { | ||
type: "object", | ||
required: ["street"], | ||
properties: { | ||
street: { | ||
type: "string", | ||
} | ||
}, | ||
}; | ||
```js | ||
var validator = new Validator({allErrors: true}); | ||
``` | ||
/** | ||
* Initialize a `Validator` instance, optionally passing in | ||
* an Ajv options object. | ||
* | ||
* @see https://github.com/ajv-validator/ajv/tree/v6#options | ||
*/ | ||
const { validate } = new Validator(); | ||
3. *Optional* - Define a bound shortcut function that can be used instead of Validator.validate | ||
```js | ||
var validate = validator.validate; | ||
/** | ||
* The `validate` method accepts an object which maps request | ||
* properties to the JSON schema you want them to be validated | ||
* against e.g. | ||
* | ||
* { requestPropertyToValidate: jsonSchemaObject } | ||
* | ||
* Validate `request.body` against `addressSchema`. | ||
*/ | ||
app.post("/address", validate({ body: addressSchema }), (request, response) => { | ||
/** | ||
* Route handler logic to run when `request.body` has been validated. | ||
*/ | ||
response.send({}); | ||
}); | ||
``` | ||
4. Use the Validator.validate method as an Express middleware, passing in an options object of the following format: | ||
```js | ||
Validator.validate({ | ||
requestProperty: schemaToUse | ||
}) | ||
``` | ||
Coming from `express-jsonschema`? Read the [migration notes](docs/migrating-from-express-jsonschema.md). | ||
Example: Validate `req.body` against `bodySchema` | ||
### Schemas in TypeScript | ||
```js | ||
app.post('/street/', validate({body: bodySchema}), function(req, res) { | ||
// route code | ||
}); | ||
If you are writing JSON schemas in TypeScript, you will need to cast your schema | ||
to the `const` type e.g. | ||
```typescript | ||
const addressSchema = { | ||
type: "object", | ||
required: ["street"], | ||
properties: { | ||
street: { | ||
type: "string", | ||
} | ||
}, | ||
} as const; | ||
``` | ||
This is required so that TypeScript doesn't attempt to widen the types of values | ||
in the schema object. If you omit the `as const` statement TypeScript will raise | ||
a compilation error. The discussion in | ||
[this issue](https://github.com/simonplend/express-json-validator-middleware/issues/39) | ||
provides further background. | ||
## 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 | ||
On encountering invalid data, the validator will call `next()` with a | ||
`ValidationError` object. It is recommended to setup a general error handler | ||
for your app where you handle `ValidationError` errors. | ||
Example - error thrown for the `body` request property | ||
Example - error thrown for the `body` request property: | ||
```js | ||
```javascript | ||
ValidationError { | ||
name: 'JsonSchemaValidationError', | ||
name: "JsonSchemaValidationError", | ||
validationErrors: { | ||
@@ -93,58 +113,60 @@ body: [AjvError] | ||
More information on [ajv#errors](https://github.com/epoberezkin/ajv#validation-errors) | ||
More information on [Ajv errors](https://github.com/ajv-validator/ajv/tree/v6#validation-errors). | ||
## Example Express app | ||
## Example Express application | ||
```js | ||
var express = require('express'); | ||
var bodyParser = require('body-parser'); | ||
```javascript | ||
import express from "express"; | ||
var { Validator, ValidationError } = require('express-json-validator-middleware'); | ||
import { Validator, ValidationError } from "express-json-validator-middleware"; | ||
const app = express(); | ||
// Initialize a Validator instance first | ||
var validator = new Validator({allErrors: true}); // pass in options to the Ajv instance | ||
app.use(express.json()); | ||
// Define a shortcut function | ||
var validate = validator.validate; | ||
const addressSchema = { | ||
type: "object", | ||
required: ["number", "street", "type"], | ||
properties: { | ||
number: { | ||
type: "number", | ||
}, | ||
street: { | ||
type: "string", | ||
}, | ||
type: { | ||
type: "string", | ||
enum: ["Street", "Avenue", "Boulevard"], | ||
}, | ||
}, | ||
}; | ||
// 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'] | ||
} | ||
} | ||
} | ||
const { validate } = new Validator(); | ||
/** | ||
* Validate `request.body` against `addressSchema`. | ||
*/ | ||
app.post("/address", validate({ body: addressSchema }), (request, response) => { | ||
/** | ||
* Route handler logic to run when `request.body` has been validated. | ||
*/ | ||
response.send({}); | ||
}); | ||
var app = express(); | ||
app.use(bodyParser.json()); | ||
// 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 | ||
res.send('valid'); | ||
/** | ||
* Error handler middleware for validation errors. | ||
*/ | ||
app.use((error, request, response, next) => { | ||
// Check the error is a validation error | ||
if (error instanceof ValidationError) { | ||
// Handle the error | ||
response.status(400).send(error.validationErrors); | ||
next(); | ||
} else { | ||
// Pass error on if not a validation error | ||
next(error); | ||
} | ||
}); | ||
// Error handler for valication errors | ||
app.use(function(err, req, res, next) { | ||
if (err instanceof ValidationError) { | ||
// At this point you can execute your error handling code | ||
res.status(400).send('invalid'); | ||
next(); | ||
} | ||
else next(err); // pass error on if not a validation error | ||
}); | ||
app.listen(3000); | ||
``` | ||
@@ -154,89 +176,114 @@ | ||
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. | ||
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. This example builds on the | ||
[Example Express application](#example-express-application). | ||
```js | ||
var TokenSchema = { | ||
type: 'object', // req.query is of type object | ||
required: ['token'], // req.query.token is required | ||
properties: { | ||
uuid: { // validate token | ||
type: 'string', | ||
format: 'uuid', | ||
minLength: 36, | ||
maxLength: 36 | ||
} | ||
} | ||
} | ||
```javascript | ||
const tokenSchema = { | ||
type: "object", | ||
required: ["token"], | ||
properties: { | ||
token: { | ||
type: "string", | ||
minLength: 36, | ||
maxLength: 36 | ||
}, | ||
}, | ||
}; | ||
app.post('/street/', Validator.validate({body: StreetSchema, query: TokenSchema}), function(req, res) { | ||
// application code | ||
}); | ||
app.post( | ||
"/address", | ||
validate({ body: addressSchema, query: tokenSchema }), | ||
(request, response) => { | ||
/** | ||
* Route handler logic to run when `request.body` and | ||
* `request.query` have both been validated. | ||
*/ | ||
response.send({}); | ||
} | ||
); | ||
``` | ||
A valid request must now include a token URL query. Example valid URL: `/street/?uuid=af3996d0-0e8b-4165-ae97-fdc0823be417` | ||
A valid request must now include a token URL query. Example valid URL: | ||
`/street/?uuid=af3996d0-0e8b-4165-ae97-fdc0823be417` | ||
## Using dynamic schema | ||
Instead of passing in a schema object you can also pass in a function that will return a schema. It is | ||
useful if you need to generate or alter the schema based on the request object. | ||
Instead of passing in a schema object you can also pass in a function that will | ||
return a schema. It is useful if you need to generate or alter the schema based | ||
on the request object. | ||
Example: loading schema from the database | ||
Example: Loading schema from a database (this example builds on the | ||
[Example Express application](#example-express-application)): | ||
// Middleware executed before validate function | ||
```js | ||
function loadSchema(req, res, next) { | ||
getSchemaFromDB() | ||
.then((schema) => { | ||
req.schema = schema; | ||
next(); | ||
}) | ||
.catch(next); | ||
```javascript | ||
function getSchemaFromDb() { | ||
/** | ||
* In a real application this would be making a database query. | ||
*/ | ||
return Promise.resolve(addressSchema); | ||
} | ||
// function that returns a schema object | ||
function getSchema(req) { | ||
// return the schema from the previous middleware or the default schema | ||
return req.schema || DefaultSchema; | ||
/** | ||
* Middleware to set schema on the `request` object. | ||
*/ | ||
async function loadSchema(request, response, next) { | ||
try { | ||
request.schema = await getSchemaFromDb(); | ||
next(); | ||
} catch (error) { | ||
next(error); | ||
} | ||
} | ||
app.post('/street/', loadSchema, Validator.validate({body: getSchema}), function(req, res) { | ||
// route code | ||
}); | ||
/** | ||
* Get schema set by the `loadSchema` middleware. | ||
*/ | ||
function getSchema(request) { | ||
return request.schema; | ||
} | ||
app.post( | ||
"/address", | ||
loadSchema, | ||
validate({ body: getSchema }), | ||
(request, response) => { | ||
/** | ||
* Route handler logic to run when `request.body` has been validated. | ||
*/ | ||
response.send({}); | ||
} | ||
); | ||
``` | ||
## Custom keywords | ||
## Ajv instance | ||
Ajv custom keywords must be defined *before* any validate() middleware | ||
The Ajv instance can be accessed via `validator.ajv`. | ||
Example: | ||
```javascript | ||
import { Validator, ValidationError } from "express-json-validator-middleware"; | ||
```js | ||
var { Validator, ValidationError } = require('express-json-validator-middleware'); | ||
var validator = new Validator({allErrors: true}); | ||
const validator = new Validator(); | ||
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() | ||
// Ajv instance | ||
validator.ajv; | ||
``` | ||
More info on custom keywords: [ajv#customs-keywords](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md#defining-custom-keywords) | ||
Ajv must be configured *before* you call `Validator.validate()` to add middleware | ||
(e.g. if you need to define [custom keywords](https://ajv.js.org/custom.html). | ||
## Ajv instance | ||
The Ajv instance can be accessed via validator.ajv. | ||
## Ajv versions | ||
```js | ||
var { Validator, ValidationError } = require('express-json-validator-middleware'); | ||
var validator = new Validator({allErrors: true}); | ||
The major version `1.x` of this module uses `ajv@5`, read their changelog and | ||
migration guide [here](https://github.com/ajv-validator/ajv/releases/tag/5.0.0). | ||
validator.ajv // ajv instance | ||
``` | ||
Major version `2.x` uses `ajv@6` in order to support draft-07 of JSON Schema. | ||
You have to manually configure Ajv to support **draft-06** schemas | ||
(see https://github.com/ajv-validator/ajv/tree/v6#using-version-6). | ||
## Tests | ||
Tests are written using Mocha & Chai | ||
Tests are written using Mocha & Chai. | ||
``` | ||
@@ -247,39 +294,11 @@ npm install | ||
## More documentation on JSON Schema | ||
## More documentation on JSON schemas | ||
- [Understanding JSON Schema](https://json-schema.org/understanding-json-schema/index.html) | ||
- [spacetelescope's understanding json schema](http://spacetelescope.github.io/understanding-json-schema/) | ||
## <a name="migrating"></a> Migrating from `express-jsonschema` | ||
In `express-jsonschema`, you could define a required property in two ways. Ajv only supports one way of doing this. | ||
```js | ||
// CORRECT | ||
{ | ||
type: 'object', | ||
properties: { | ||
foo: { | ||
type: 'string' | ||
} | ||
}, | ||
required: ['foo'] // <-- correct way | ||
} | ||
// WRONG | ||
{ | ||
type: 'object', | ||
properties: { | ||
foo: { | ||
type: 'string', | ||
required: true // nono way | ||
} | ||
} | ||
} | ||
``` | ||
## Credits | ||
- Maintained by [@JouzaLoL](https://github.com/jouzalol) | ||
- [Original Module](https://github.com/trainiac/express-jsonschema) by [@trainiac](https://github.com/trainiac) | ||
- PRs: see Contributors | ||
- Maintained by [@simonplend](https://github.com/simonplend/) | ||
- Created and previously maintained by [@vacekj](https://github.com/vacekj/) | ||
- Thank you to all of this project's [contributors](https://github.com/vacekj/express-json-validator-middleware/graphs/contributors) | ||
- Based on the [express-json-schema](https://github.com/trainiac/express-jsonschema) library by [@trainiac](https://github.com/trainiac) |
@@ -0,4 +1,5 @@ | ||
import { Request } from "express"; | ||
import { RequestHandler } from "express-serve-static-core"; | ||
import { JSONSchema4, JSONSchema6, JSONSchema7 } from "json-schema"; | ||
import { ErrorObject, Options as AjvOptions } from "ajv"; | ||
import { Ajv, ErrorObject, Options as AjvOptions } from "ajv"; | ||
@@ -12,4 +13,3 @@ declare module "express-json-validator-middleware" { | ||
export type ValidateFunction = | ||
| Function | ||
type AllowedSchema = | ||
| JSONSchema4 | ||
@@ -19,5 +19,11 @@ | JSONSchema6 | ||
export type ValidateFunction = | ||
| ((req: Request) => AllowedSchema) | ||
| AllowedSchema; | ||
export class Validator { | ||
constructor(options: AjvOptions); | ||
ajv: Ajv; | ||
validate(rules: List<ValidateFunction>): RequestHandler; | ||
@@ -27,4 +33,5 @@ } | ||
export class ValidationError extends Error { | ||
public validationErrors: List<ErrorObject>; | ||
constructor(validationErrors: List<ErrorObject[]>); | ||
public validationErrors: List<ErrorObject[]>; | ||
} | ||
} |
@@ -0,0 +0,0 @@ var Ajv = require("ajv"); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
7633419
10
13
226043
301
2