Swagger: Validate input based on Swagger annotations

Created on 6 Dec 2018  路  7Comments  路  Source: nestjs/swagger

I'm submitting a...


[ ] Regression 
[ ] Bug report
[X] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

You have to duplicate annotations with both class validator AND swagger to get input validation and swagger support. Seems like we should be able to validate input based on the swagger annotations - they compile to JSON Schema.

I'm guessing this isn't already possible as it would be in the recipe

Environment


Nest version: 5.4.1

Most helpful comment

Ok, I have a quick and dirty solution for now.
Keep in mind that my goal is to minimize code duplication, and as class-validator is basically documenting the same way swagger annotations would, and that I need class-validator, I focused on this solution. But it may not fit your needs.

So, for now you can use https://github.com/epiphone/class-validator-jsonschema, and this won't require any change on @nest/swagger side :

Main.ts :

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './modules/app.module';
import dotenv = require('dotenv-extended');
import * as cors from 'cors';
import { getFromContainer, MetadataStorage } from 'class-validator';
import { validationMetadatasToSchemas } from 'class-validator-jsonschema';

import config from './config';

async function bootstrap() {
  dotenv.load();
  const app = await NestFactory.create(AppModule);

  app.use(cors(config.cors));

  // swagger
  const swaggerConfig = new DocumentBuilder().setTitle('Vaneau neuf swagger').build();

  const document = SwaggerModule.createDocument(app, swaggerConfig);

  // Creating all the swagger schemas based on the class-validator decorators
  const metadatas = (getFromContainer(MetadataStorage) as any).validationMetadatas;
  const schemas = validationMetadatasToSchemas(metadatas);
  document.definitions = schemas;

  SwaggerModule.setup(config.swagger.route, app, document);

  await app.listen(3001);
}
bootstrap();

Dtos :

import { Min, Max, IsUUID, IsArray, ValidateNested, IsString } from 'class-validator';

export class TestDTO {
  @IsUUID()
  public id: string;

  @IsArray()
  @IsString({ each: true })
  public wishes: string[];

  @Min(10)
  public surface: number;
}

// tslint:disable-next-line:max-classes-per-file
export class OtherTestDTO {
  @IsString()
  public firstname: string;

  @IsString()
  public lastname: string;

  @Min(0)
  @Max(99)
  public age: number;

  @ValidateNested()
  public test: TestDTO;
}

Example controller :

import { Controller, Get, HttpStatus, Post, Body } from '@nestjs/common';
import { ApiResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { TestDTO, OtherTestDTO } from './testDTO';

@Controller()
export class AppController {
  @Get('/hello')
  @ApiOkResponse({ description: 'A test route', type: OtherTestDTO })
  @ApiUnauthorizedResponse({ description: 'An error' })
  getHelloWorld(): OtherTestDTO {
    return {
      age: 98,
      firstname: 'jos茅',
      lastname: 'pr茅vot',
      test: { id: 'lol', surface: 4695, wishes: [] },
    };
  }

  @Post()
  @ApiOkResponse({ description: 'The newly created item', type: TestDTO })
  @ApiUnauthorizedResponse({ description: 'An error' })
  postHello(@Body() createCatDto: OtherTestDTO): TestDTO {
    return { id: 'lol', surface: 4695, wishes: [] };
  }
}

Result :

image

All 7 comments

There's a very old conversation about this over at https://github.com/typestack/class-validator/issues/5

Ideally, it'd be nice if the solution that we came up for this was _not_ Nest-specific (and could be used anywhere that class-validator is used).

Jumping in.
I discovered https://github.com/epiphone/class-validator-jsonschema which may fill the gap ? We are using class-validator quite extensively.
I see that you are thinking about the other way around, not sure which way is the best.

Ok, I have a quick and dirty solution for now.
Keep in mind that my goal is to minimize code duplication, and as class-validator is basically documenting the same way swagger annotations would, and that I need class-validator, I focused on this solution. But it may not fit your needs.

So, for now you can use https://github.com/epiphone/class-validator-jsonschema, and this won't require any change on @nest/swagger side :

Main.ts :

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './modules/app.module';
import dotenv = require('dotenv-extended');
import * as cors from 'cors';
import { getFromContainer, MetadataStorage } from 'class-validator';
import { validationMetadatasToSchemas } from 'class-validator-jsonschema';

import config from './config';

async function bootstrap() {
  dotenv.load();
  const app = await NestFactory.create(AppModule);

  app.use(cors(config.cors));

  // swagger
  const swaggerConfig = new DocumentBuilder().setTitle('Vaneau neuf swagger').build();

  const document = SwaggerModule.createDocument(app, swaggerConfig);

  // Creating all the swagger schemas based on the class-validator decorators
  const metadatas = (getFromContainer(MetadataStorage) as any).validationMetadatas;
  const schemas = validationMetadatasToSchemas(metadatas);
  document.definitions = schemas;

  SwaggerModule.setup(config.swagger.route, app, document);

  await app.listen(3001);
}
bootstrap();

Dtos :

import { Min, Max, IsUUID, IsArray, ValidateNested, IsString } from 'class-validator';

export class TestDTO {
  @IsUUID()
  public id: string;

  @IsArray()
  @IsString({ each: true })
  public wishes: string[];

  @Min(10)
  public surface: number;
}

// tslint:disable-next-line:max-classes-per-file
export class OtherTestDTO {
  @IsString()
  public firstname: string;

  @IsString()
  public lastname: string;

  @Min(0)
  @Max(99)
  public age: number;

  @ValidateNested()
  public test: TestDTO;
}

Example controller :

import { Controller, Get, HttpStatus, Post, Body } from '@nestjs/common';
import { ApiResponse, ApiOkResponse, ApiUnauthorizedResponse } from '@nestjs/swagger';
import { TestDTO, OtherTestDTO } from './testDTO';

@Controller()
export class AppController {
  @Get('/hello')
  @ApiOkResponse({ description: 'A test route', type: OtherTestDTO })
  @ApiUnauthorizedResponse({ description: 'An error' })
  getHelloWorld(): OtherTestDTO {
    return {
      age: 98,
      firstname: 'jos茅',
      lastname: 'pr茅vot',
      test: { id: 'lol', surface: 4695, wishes: [] },
    };
  }

  @Post()
  @ApiOkResponse({ description: 'The newly created item', type: TestDTO })
  @ApiUnauthorizedResponse({ description: 'An error' })
  postHello(@Body() createCatDto: OtherTestDTO): TestDTO {
    return { id: 'lol', surface: 4695, wishes: [] };
  }
}

Result :

image

This will be solved by #191. Let's track this there

Ok, I have a quick and dirty solution for now.

I have changed your code a bit. @nestjs/swagger has changed a bit and this is the most recent working code for me:

import {getFromContainer, MetadataStorage} from 'class-validator';
import {validationMetadatasToSchemas} from 'class-validator-jsonschema';
....
const metadata = (getFromContainer(MetadataStorage) as any).validationMetadatas;
document.components.schemas = Object.assign({}, document.components.schemas || {}, validationMetadatasToSchemas(metadata));

Make sure to install the following

npm install class-validator class-transformer class-validator-jsonschema

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mogusbi picture mogusbi  路  3Comments

KatSick picture KatSick  路  3Comments

leefordjudes picture leefordjudes  路  4Comments

vh13294 picture vh13294  路  4Comments

otroboe picture otroboe  路  3Comments