express-openapi-validator icon indicating copy to clipboard operation
express-openapi-validator copied to clipboard

Separate out validation from express

Open wparad opened this issue 3 years ago • 0 comments

I'm using a framework (AWS API gateway) that isn't one that is supported directly, and while I'm not asking for that framework to be supported, there is a lot of great functionality that I want to reuse, but can't really.

Luckily there is a workaround, but isn't great as it still loads a ton of unnecessary functionality, causing slowness ~80ms of unnecessary delay on initial load.

The solution

Separate the middlewares to their own package with the configuration, and have the express-openapi-validator, depend on that (instead of openapi-core)

In the meantime we've written this:

const { OpenApiValidator } = require('express-openapi-validator/dist/openapi.validator');
const { OpenApiSpecLoader } = require('express-openapi-validator/dist/framework/openapi.spec.loader');
const { cloneDeep } = require('lodash');

const spec = require('./openapi');
const logger = require('./requestLogger');

const oav = new OpenApiValidator({ apiSpec: spec, validateResponses: false, validateApiSpec: false, validateSecurity: false, validateRequests: { allowUnknownQueryParameters: false } });
const compiledSpec = new OpenApiSpecLoader({ apiDoc: cloneDeep(spec), validateApiSpec: false, $refParser: { mode: 'dereference' } }).load();
const requestValidationFunction = oav.installMiddleware(compiledSpec).find(m => m.name === 'requestMiddleware');

class ModelValidator {
  async validate(request) {
    const resolvedSpec = await compiledSpec;
    const schema = resolvedSpec.apiDoc.paths[request.route] && resolvedSpec.apiDoc.paths[request.route][request.httpMethod.toLowerCase()];
    if (!schema) {
      return;
    }

    const newRequest = {
      originalUrl: request.path,
      method: request.httpMethod,
      headers: Object.assign({ 'content-type': 'application/json' }, request.headers || {}),
      query: request.queryStringParameters,
      body: request.body,
      params: request.pathParameters,
      openapi: {
        expressRoute: request.route.replace(/{([^}]+)}/g, ':$1'),
        schema: schema,
        pathParams: {}
      }
    };

      await requestValidationFunction(newRequest, {}, error => {
        if (error) {
          const wrapped = Error.create({ title: `Request ${error.errors[0].path} ${error.errors[0].message}.` });
          wrapped.code = 'InvalidInputRequest';
          throw wrapped;
        }
      });

    return;
  }

Any wisdom on improvements would be appreciated.

wparad avatar Jun 02 '21 14:06 wparad