Giter Site home page Giter Site logo

simonplend / express-json-validator-middleware Goto Github PK

View Code? Open in Web Editor NEW
158.0 6.0 26.0 1.95 MB

Express middleware for validating requests against JSON schema

Home Page: https://npm.im/express-json-validator-middleware

License: MIT License

JavaScript 100.00%
expressjs express-middleware json json-schema ajv json-schemas express

express-json-validator-middleware's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

express-json-validator-middleware's Issues

Add ability to return Promises from schema generator functions

Exactly this. Having to create another middleware just to return a "precursor" schema, or some component of a schema, is potentially tedious and error-prone (what happens if I forget to add the "precursor" middleware to my route?), and can result in related logic being split into unrelated places.

To this end, an enhancement I'd appreciate is the ability to return a Promise from a dynamic schema generator function. Then, the middleware can await the Promise (or the synchronous return, in the existing case) and do the usual compile -> validate -> next() thing.

Allow Promises of schemas

This is a retread of #31, but with some actually concrete proposals and rationale.

Currently, it is only possible to provide either literal schemas, or schemas from synchronous functions. If a schema requires (e.g.) a database read, the only way to do it is to perform the operation in a prior middleware, then pass it along by mutating the request object (or to wrap this middleware in something custom). This is sub-optimal for a few reasons:

  1. It separates operations that should be performed together. Having to put together two separate middlewares to perform your validation cycle makes it highly likely that you'll eventually forget one and break something.
  2. Modifying the request object is problematic in TypeScript. There's no good place to attach a random value of arbitary type, and you lose the type information in the following middlewares.
  3. Multiple middlewares modifying the request object may stomp each other's keys, producing an unknown state by the time it reaches the schema generator.

My proposed solution to this is fairly straightforward: allow passing Promises of a schema, or functions that return Promises of a schema. In TypeScript terms, changing this (borrowed from #72 for easier readability):

	type AllowedSchema =
		| JSONSchema4
		| JSONSchema6
		| JSONSchema7;

	export type ValidateFunction =
		| ((req: Request) => AllowedSchema)
		| AllowedSchema;

to this:

	type AllowedSchema =
		| JSONSchema4
		| JSONSchema6
		| JSONSchema7;

	export type ValidateFunction =
		| ((req: Request) => AllowedSchema | Promise<AllowedSchema>)
		| Promise<AllowedSchema>
		| AllowedSchema;

Then, the request handler can await the Promise, and perform the normal operation.

README Number Rendering

In the npm and yarn docs the readme numbering is not incrementing correctly like it does for github in the getting started section

Restrict published files for this package

The v2.2.0 release of this package accidentally included two large unrelated JSON files. This bloated the package size from 37.9 kB to 7.63 MB.

We should set a files field in package.json to restrict which files are published for this package. We can then remove the .npmignore file, as it's not a safe way of controlling what is published.

Contents of v2.2.0 release tarball

$ curl -s (npm view [email protected] dist.tarball) | tar tzvf -

-rw-r--r-- 0/0             375 1985-10-26 09:15 package/.editorconfig
-rw-r--r-- 0/0            1089 1985-10-26 09:15 package/LICENSE
-rw-r--r-- 0/0            1902 1985-10-26 09:15 package/src/index.js
-rw-r--r-- 0/0              59 1985-10-26 09:15 package/.prettierrc.json
-rw-r--r-- 0/0         3760933 1985-10-26 09:15 package/file:/home/simonplend/.config/Code/User/globalStorage/amazonwebservices.aws-toolkit-vscode/cloudformation.schema.json
-rw-r--r-- 0/0            1435 1985-10-26 09:15 package/package.json
-rw-r--r-- 0/0         3854508 1985-10-26 09:15 package/file:/home/simonplend/.config/Code/User/globalStorage/amazonwebservices.aws-toolkit-vscode/sam.schema.json
-rw-r--r-- 0/0             525 1985-10-26 09:15 package/docs/migrating-from-express-jsonschema.md
-rw-r--r-- 0/0            8431 1985-10-26 09:15 package/README.md
-rw-r--r-- 0/0            2695 1985-10-26 09:15 package/patreon.png
-rw-r--r-- 0/0             830 1985-10-26 09:15 package/src/index.d.ts
-rw-r--r-- 0/0             636 1985-10-26 09:15 package/.github/workflows/ci.yml
-rw-r--r-- 0/0               1 1985-10-26 09:15 package/.github/FUNDING.yml

Performance improvements

Investigate into possible performance improvements:

- Best way to compile the schema and check for errors

error handler cannot send custom error

It appears to me you might have an issue with calling a function that sets the headers in your error path, because whenever I try to change the error status in the error handler I get:

cannot 400 after headers sent

The documentation on this stuff is pretty confusing. I'm returning promises in my API handler and there's no error being thrown there. My error handler looks like this:

app.use(function(err, req, res, next) {
  console.log(JSON.stringify(err));
  if (err && err.name && err.name  == 'JsonSchemaValidationError') {
    console.log('-------- sending 400: ', res.headersSent);   // show false
    //return res.sendStatus(400).json(err.validationErrors).end();
    return res.json(err.validationErrors).sendStatus(400);
  } else {
    log.info({err: err}, 'uncaught exception');
    return res.sendStatus(500);
  }
});


Schema properties default value

I was wondering if the library supports JSON Schema default values, and if so how would one use them?

I've tried simply setting defaults in the schema declaration, however after passing the validation middleware and trying to access the properties on the effect ( logic middleware ) the properties still dont exist.

Am I doing something wrong or is it not supported / implemented?

const schema = {
type: 'object',
required: ['foo'],
properties: {
foo: { type: 'string' },
bar: { type: 'string', default: 'Bar' }
}
};
app.post('/foo', validate({body: schema}), (req, res) => {
console.log(req.data);
});

Above will output { foo: "Foo" } for a post with the following data sent { "foo": "Foo" }, while I would expect it should print { foo: "Foo", bar: "Bar" }

Add support for readOnly and writeOnly keywords

It would be great to have support for readOnly and writeOnly keywords as described on the OpenApi site

You can use the readOnly and writeOnly keywords to mark specific properties as read-only or write-only. This is useful, for example, when GET returns more properties than used in POST โ€“ you can use the same schema in both GET and POST and mark the extra properties as readOnly. readOnly properties are included in responses but not in requests, and writeOnly properties may be sent in requests but not in responses.

Dynamic Schema "Error: schema should be object or boolean"

I'm trying to use dynamic schemas as described in the documentation:

  function loadAccountSchema(req, res, next) {

    try {
      if (req.body.accountType === 'personal') {
        req.schema = accountSchema.Insert;
      }
      else {
        req.schema = accountSchema.InsertCorporate;
      }
      next();
    } catch (error) {
      next(error);
    }
  }

  function getSchema(req) {
  	// return the schema from the previous middleware or the default schema
      return req.schema || DefaultSchema;
  }


  app.post('/account/:uId', loadAccountSchema, validate({ body: getSchema }), function(
    req, res ) {

However I get this error when I try to run express:

/home/solomon/Development/trippinc/firebase-backend/src/main/functions/node_modules/ajv/lib/ajv.js:303
    throw new Error('schema should be object or boolean');
    ^

Error: schema should be object or boolean
    at Ajv._addSchema (/home/solomon/Development/trippinc/firebase-backend/src/main/functions/node_modules/ajv/lib/ajv.js:303:11)
    at Ajv.compile (/home/solomon/Development/trippinc/firebase-backend/src/main/functions/node_modules/ajv/lib/ajv.js:117:24)
    at Validator.<anonymous> (/home/solomon/Development/trippinc/firebase-backend/src/main/functions/node_modules/express-json-validator-middleware/src/index.js:24:36)
    at Array.map (native)
    at Validator.validate (/home/solomon/Development/trippinc/firebase-backend/src/main/functions/node_modules/express-json-validator-middleware/src/index.js:22:50)
    at module.exports (/home/solomon/Development/trippinc/firebase-backend/src/main/functions/usermgmt.js:86:48)

Any ideas whats going on?

Error when starting an express server

Hi! I would like to ask about this Error i'm currently having on my project.

const validateFunctions = Object.keys(options).map(function (requestProperty) {
                                                      ^

TypeError: Cannot convert undefined or null to object
    at Function.keys (<anonymous>)
    at Validator.validate (C:\xampp\htdocs\siakad-new\node_modules\express-json-validator-middleware\src\index.js:24:42)
    at Object.<anonymous> (C:\xampp\htdocs\siakad-new\routes\users_route.js:25:54)
    at Module._compile (internal/modules/cjs/loader.js:778:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
[nodemon] app crashed - waiting for file changes before starting...

I tried to do exactly the same approach like in the README, but still got this Error.

const { validate } = require('../config/validator');

router.get('/:id', validate({ params: UsersSchema.getUsersById.params }), validate(UsersSchema.getUserById), UsersController.getUserById);

Am i missing something here actually?

Any replies would be helpful.

Stop on first error

Originating from #15
While it may provide a performance benefit (not having to validate after the first error is found), from my experience the user usually wants to work with errors from all req properties.

If we implement this feature, I would like to make it configurable by introducing an options object for the Validator instance, and also make it off by default.

Thoughts?

Type error when using example with Typescript

I've tried the library and it works perfectly with normal js, but throws type errors when using ts.

The type error occurs at addressSchema when passing it to body. Is there something I missed?

Type '{ readonly type: "object"; readonly required: readonly ["number", "street", "type"]; readonly properties: { readonly number: { readonly type: "number"; }; readonly street: { readonly type: "string"; }; readonly type: { ...; }; }; }' is not assignable to type 'ValidateFunction | undefined'.
Type '{ readonly type: "object"; readonly required: readonly ["number", "street", "type"]; readonly properties: { readonly number: { readonly type: "number"; }; readonly street: { readonly type: "string"; }; readonly type: { ...; }; }; }' is not assignable to type 'JSONSchema7'.
Types of property 'required' are incompatible.
The type 'readonly ["number", "street", "type"]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.

import express from "express";
import { Validator, ValidationError } from "express-json-validator-middleware";

const app = express();

app.use(express.json());

const addressSchema = {
  type: "object",
  required: ["number", "street", "type"],
  properties: {
    number: {
      type: "number",
    },
    street: {
      type: "string",
    },
    type: {
      type: "string",
      enum: ["Street", "Avenue", "Boulevard"],
    },
  },
} as const;

const { validate } = new Validator({});

app.post("/address", validate({ body: addressSchema }), (request, response) => {
  response.send({});
});
app.listen(3000);

Error: unknown format "email"

Hello,

I'm trying to use the email format, but no success.

Here's the schema:
{ "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" } } }

Then when the server is starting (even before any request), I get this error:
Error: unknown format "email" ignored in schema at path "#/properties/email"

My Validator is being stantiated with this:
const { validate } = new Validator({ allErrors: true });

If I remove "format": "email" from the schema, validation works fine.

What I'm doing wrong?

Edit: already installed ajv-formats package too, but no success; I don't know how to add the formats I would like to use and connect with the middleware.

Date-time validation?

I'm new to JSON schema validation, but according to this website there is a date-time type. But I get an error when I use this in my schema declaration.

module.exports = {
  type: 'object',

  properties: {
    startedAt: {
      type: 'date-time',
    },

  // ...
};

error:
Error: schema is invalid: data.properties['startedAt'].type should be equal to one of the allowed values, data.properties['startedAt'].type should be array, data.properties['startedAt'].type should match some schema in anyOf

Is there a documentation saying which types are supported?

Regex Support

Would be great if you could provide regex support as part of the middleware. So, I can specify my own rules for certain validations.

TypeScript issue in ValidationError instance

I found out that ValidationError is extended from the Error instance.
If you do this below in TypeScript:

const validationError = new ValidationError({
  params: [{
    dataPath: '.id',
    keyword: 'maxLength',
    message: 'should NOT be longer than 2 characters',
    params: { limit: 2 },
    schemaPath: '#/properties/id/maxLength'
  }]
})

It will throw a ts compile error because the argument type should be a string.
Screen Shot 2021-08-11 at 2 10 07 PM

To fix this problem, I just add // @ts-ignore before new ValidationError.

Any better support for this?

Upgrade Ajv dependency to v8

This library is currently using Ajv v6. Upgrading to Ajv v7 was proposed in pull request #55. In the meantime, Ajv v8 has been released, so we'll upgrade to that.

See: Changes from Ajv v6.12.6 to v8.0.0

TODO

Some of these may be worth breaking out into separate issues.

  • Upgrade Ajv dependency to latest release (v8.8.2)
    • Run tests
    • Install in test application and test manually
  • Identify breaking changes between Ajv v6 and v8 which directly affect this library (none)

How to add custom errors to?

Hi,

I've been trying for a while to setup so I can post a custom errormessage ith the validationErrors but I can't seem to get it to work. I'm not sure what I'm doing wrong. Is there any examples of this?

Import everything;

const { Validator } = require('express-json-validator-middleware');
const { validate } = new Validator();

Create the scheme to validate against,

exports.test = {
	type: 'object',
	required: ['name'],
	properties: {
		name: {
			type: 'string',
		},
	},
	errorMessage: 'This is the message I want to pass on and overwrite the standard one..',
};

Set it up as a middleware
router.get('/test', validate({ body: userValidator.test }), userController.test);

And this is the response,

"error": {
    "name": "JsonSchemaValidationError",
    "validationErrors": {
      "body": [
        {
          "keyword": "required",
          "dataPath": "",
          "schemaPath": "#/required",
          "params": {
            "missingProperty": "name"
          },
          "message": "should have required property 'name'"
        }
      ]
    },

I tried following this: https://github.com/simonplend/express-json-validator-middleware#ajv-instance, but that didn't solve it (honestly the ajv-instance just crashes the server.

Running a node server (v14.15.4), with express(v4.17.1)

Most likely I'm missing out some really basic part but an anyone help me out with how to get this package working with a custom errormessage?

Thanks in advance!

2.1.0 breaks TS build

2.0.0 works great but once updated to 2.1.0 I got strange errors from ts compiler.

Type '{ type: string; required: string[]; properties: { firstName: { type: string; }; lastName: { type: string; }; email: { type: string; }; phone: { type: string[]; }; password: { type: string; }; participantId: { type: string; }; }; }' is not assignable to type 'ValidateFunction'.
  Type '{ type: string; required: string[]; properties: { firstName: { type: string; }; lastName: { type: string; }; email: { type: string; }; phone: { type: string[]; }; password: { type: string; }; participantId: { type: string; }; }; }' is not assignable to type 'JSONSchema7'.
    Types of property 'type' are incompatible.
      Type 'string' is not assignable to type '"string" | "number" | "boolean" | "object" | "integer" | "null" | "array" | JSONSchema7TypeName[]'.

Allow providing an Ajv instance to Validator

AJV has a plugin system, along the lines of the decorator pattern - each plugin exposes a function that modifies an instantiated Ajv object. However, because the Validator class only accepts AjvOptions and then constructs its own, applying plugins requires modifying it at runtime. This is fine (and expected, according to the tests) in straight JavaScript, but in TypeScript violates the provided types, because the Ajv is not included in the class definition:

// Plugin
import formats from "ajv-formats-draft2019";

const ajv = formats(new Ajv({allErrors: true}));
const validator = new Validator({});
validator.ajv = ajv;

// Yields: 
// TS2339: Property 'ajv' does not exist on type 'Validator'.

Either way, this seems a brittle approach, prone to breaking as the library changes. A backwards-compatible solution to this might be something like:

export class Validator {
    // Allow passing an Ajv to the constructor
    constructor(options: AjvOptions | Ajv);

    // Expose the ajv to match the behaviour of the JS implementation
    ajv: Ajv;
}

If a non-Ajv object is provided, the existing behaviour is kept, but otherwise the validator can just use the provided Ajv wholesale.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.