Harold L. Brown
Harold L. Brown

Reputation: 9976

How to install Express middleware (express-openapi-validator) in NestJS?

I am writing a NestJS application. Now I want to install the Express middleware express-openapi-validator.

However, I can't get it to work. There is a description for how to install the express-openapi-validator in express, but it always results in errors.

For example

export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer) {
        consumer.apply(middleware({apiSpec "./bff-api.yaml"}))
            .forRoutes(OrganizationController)
    }
}

results in

error TS2345: Argument of type 'OpenApiRequestHandler[]' is not assignable to parameter of type 'Function | Type<any>'.
      Type 'OpenApiRequestHandler[]' is missing the following properties from type 'Type<any>': apply, call, bind, prototype, and 4 more.

How can I install this middleware in NestJS?

Upvotes: 3

Views: 1643

Answers (2)

Arno Hilke
Arno Hilke

Reputation: 1139

I added a NestJS example to express-openapi-validator (static link for posterity).

The AppModule looks basically identical, although you don't need to iterate over the middlewares:

@Module({
  imports: [PingModule],
  providers: [{ provide: APP_FILTER, useClass: OpenApiExceptionFilter }],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(
        ...OpenApiValidator.middleware({
          apiSpec: join(__dirname, './api.yaml'),
        }),
      )
      .forRoutes('*');
  }
}

I also added an exception filter to convert the error from express-openapi-validator to a proper response; otherwise I would always get a 500 error. You could also use this approach to convert the error into a custom error format.

import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
import { Response } from 'express';
import { error } from 'express-openapi-validator';

@Catch(...Object.values(error))
export class OpenApiExceptionFilter implements ExceptionFilter {
  catch(error: ValidationError, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();

    response.status(error.status).json(error);
  }
}

interface ValidationError {
  status: number;
  message: string;
  errors: Array<{
    path: string;
    message: string;
    error_code?: string;
  }>;
  path?: string;
  name: string;
}

Upvotes: 4

Harold L. Brown
Harold L. Brown

Reputation: 9976

I have now got it working:

configure(consumer: MiddlewareConsumer) {
    middleware({
        apiSpec: `${__dirname}/../api-doc/bff-api.yaml`
    }).forEach(value => consumer.apply(value).forRoutes(OrganizationController))
}

Upvotes: 1

Related Questions