express-json-validator-middleware
Advanced tools
Comparing version
@@ -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> | ||
[](https://travis-ci.org/JouzaLoL/express-json-validator-middleware) | ||
[](https://codecov.io/gh/JouzaLoL/express-json-validator-middleware) | ||
[](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[](https://www.npmjs.com/package/express-json-validator-middleware) | ||
> [Express](https://github.com/expressjs/express/) middleware for validating | ||
requests against JSON schemas. | ||
<hr> | ||
[](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[](https://www.npmjs.com/package/express-json-validator-middleware) | ||
[](https://github.com/vacekj/express-json-validator-middleware/actions?query=workflow%3A%22Node.js+CI%22) | ||
[](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
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
7633419
44074.88%10
-9.09%13
62.5%226043
219359.22%301
6.74%2
Infinity%