PayU / openapi-validator-middleware

Input validation using Swagger (Open API) and ajv
Apache License 2.0
144 stars 50 forks source link

openapi-validator-middleware

NPM Version main workflow NPM Downloads Test Coverage Known Vulnerabilities Apache 2.0 License

This package provides data validation within an Express, Koa or Fastify app according to a Swagger/OpenAPI definition. It uses Ajv under the hood for validation.

NOTICE: As this package gone through a long way, as we added support for OpenAPI definitions, while also adding support for more frameworks such as Koa and Fastify, we finally took the step of changing express-ajv-swagger-validation name to something that describes it better. As of now we'll be using the name openapi-validator-middleware instead.
There are no code changes in openapi-validator-middleware@2.0.0 compared to express-ajv-swagger-validation@1.2.0 apart from the name change.

Table of Contents

Installation

Install using the node package registry:

npm install --save openapi-validator-middleware

Then import the module in your code:

const swaggerValidation = require('openapi-validator-middleware');

API

openapi-validator-middleware.validate(fastifyOptions)

This middleware function validates the request body, headers, path parameters and query parameters according to the paths definition of the swagger file. Make sure to use this middleware inside a route definition in order to have req.route.path assigned to the most accurate express route.

fastifyOptions

openapi-validator-middleware.init(pathToSwaggerFile, options)

Initialize the middleware using a swagger definition. The function executes synchronously and does not return anything.

openapi-validator-middleware.initAsync(pathToSwaggerFile, options)

Initialize the middleware using a swagger definition. The function executes asynchronously and the resolved promise does not return anything.

This Initilaztion function also supports schema with references to external files.

Options

Options currently supported:

Usage Example

Express

swaggerValidation.init('test/unit-tests/input-validation/pet-store-swagger.yaml');
const app = express();
app.use(bodyParser.json());
app.get('/pets', swaggerValidation.validate, (req, res, next) => {
    return res.json({ result: 'OK' });
});
app.post('/pets', swaggerValidation.validate, (req, res, next) => {
    return res.json({ result: 'OK' });
});
app.get('/pets/:petId', swaggerValidation.validate, (req, res, next) => {
    return res.json({ result: 'OK' });
});

app.use((err, req, res, next) => {
    if (err instanceof swaggerValidation.InputValidationError) {
        return res.status(400).json({ more_info: JSON.stringify(err.errors) });
    }
});

const server = app.listen(serverPort, () => {});

Koa

'use strict';
const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-bodyparser');
const inputValidation = require('openapi-validator-middleware');
const app = new Koa();
const router = new Router();
app.use(bodyParser());
app.use(router.routes());
inputValidation.init('test/pet-store-swagger.yaml', { framework: 'koa' });

router.get('/pets', inputValidation.validate, async (ctx, next) => {
    ctx.status = 200;
    ctx.body = { result: 'OK' };
});
router.post('/pets', inputValidation.validate, async (ctx, next) => {
    ctx.status = 200;
    ctx.body = { result: 'OK' };
});
router.get('/pets/:petId', inputValidation.validate, async (ctx, next) => {
    ctx.status = 200;
    ctx.body = { result: 'OK' };
});
router.put('/pets', inputValidation.validate, async (ctx, next) => {
    ctx.status = 200;
    ctx.body = { result: 'OK' };
});

app.use(async (ctx, next) => {
    try {
        return await next();
    } catch (err) {
        if (err instanceof inputValidation.InputValidationError) {
            ctx.status = 400;
            ctx.body = err.errors;
        }
        throw err;
    }
})
app.listen(process.env.PORT || 8888, function () {
    console.log('listening on', this.address());
});

Fastify

'use strict';
const fastify = require('fastify');
const inputValidation = require('openapi-validator-middleware');

async function getApp() {
    inputValidation.init('test/pet-store-swagger.yaml', { 
        framework: 'fastify'
    });
    const app = fastify({ logger: true });

    app.register(inputValidation.validate({
      skiplist: ['^/_metrics$']
    }));
    app.setErrorHandler(async (err, req, reply) => {
        if (err instanceof inputValidation.InputValidationError) {
             return reply.status(400).send({ more_info: JSON.stringify(err.errors) });
        }

        reply.status(500);
        reply.send();
    });

    app.get('/pets', (req, reply) => {
        reply.status(204).send();
    });

    await app.ready();
    return app;
}

multiple-instances

const inputValidation = require('openapi-validator-middleware');
const validatorA = inputValidation.getNewMiddleware('test/pet-store-swaggerA.yaml', {framework: 'express'});
const validatorB = inputValidation.getNewMiddleware('test/pet-store-swaggerB.yaml', {framework: 'express'});

app.get('/pets', validatorA.validate, (req, res, next) => {
    return res.json({ result: 'OK' });
});

app.post('/pets', validatorB.validate, (req, res, next) => {
    return res.json({ result: 'OK' });
});

Important Notes

Schema Objects

It is important to set the type property of any Schema Objects explicitly to object. Although it isn't required in the OpenAPI specification, it is necessary in order for Ajv to work correctly.

Multipart/form-data (files)

Multipart/form-data (files) support is based on express/multer.

Fastify support

Fastify support requires uri-js dependency to be installed. Make sure to run npm install uri-js. When using this package as a middleware for fastify, the validations errors will be thrown.

Koa support

When using this package as middleware for koa, the validations errors will be thrown.

Koa packages

This package supports Koa servers that use koa-router, koa-bodyparser and koa-multer.

Known Issues with OpenAPI 3

Running Tests

The tests use mocha, istanbul and mochawesome. Run them using the node test script:

npm test