Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Query() does not transform to DTO

Tags:

nestjs

I have a controller that needs to receive data in the request query string (I can't use the body because I'm interacting with a legacy system).

I wrote a DTO map query params to an object and I'm using a ValidationPipe to validate and transform the data to my DTO.

So, I have this:

import { Get, Controller, Query, Post, Body, UsePipes, ValidationPipe } from '@nestjs/common';

class TestDto {
  @IsNumber()
  field1: number;
  @IsBoolean()
  field2: boolean;
}

@Controller()
export class AppController {
  constructor() {}

  @Get()
  @UsePipes(new ValidationPipe({ whitelist: false, transform: true}))
  root(@Query() dto: TestDto): TestDto {
    return dto;
  }

}

All of the previous code compìles and follows the NestJS documentation, but when I call http://localhost:3000/?field1=15&field2=true I get this:

{
    "statusCode": 400,
    "error": "Bad Request",
    "message": [
        {
            "target": {
                "field1": "15",
                "field2": "true"
            },
            "value": "15",
            "property": "field1",
            "children": [],
            "constraints": {
                "isNumber": "field1 must be a number"
            }
        },
        {
            "target": {
                "field1": "15",
                "field2": "true"
            },
            "value": "true",
            "property": "field2",
            "children": [],
            "constraints": {
                "isBoolean": "field2 must be a boolean value"
            }
        }
    ]
}

Both fields are valid according to the attributes but the pipe rejects the request. If I change from @IsNumber to @IsNumberString and from @IsBoolean to @IsBooleanString it validates, but I do not received the transformed data (i.e. I get a plain object instead of my DTO)

Did anybody face something like this?

like image 531
Jose Selesan Avatar asked Oct 14 '19 13:10

Jose Selesan


3 Answers

It cannot, as interfaces only shape your structure or tell something about type. You validation will not work correctly due to the same fact.

class TestDto

See NestJS docs - Auto Validation NestJS docs - Payload transforming

As example in docs say:

import { IsEmail, IsNotEmpty } from 'class-validator'; // 'class'

export class CreateUserDto { // notice class
  @IsEmail()
  email: string;

  @IsNotEmpty()
  password: string;
}

Update #1 - tell validator to try to make implicit conversion

@UsePipes( new ValidationPipe( { transform: true, transformOptions: {enableImplicitConversion: true} }))

Update #2 - use custom @Query() param decorator

import { Controller, createParamDecorator, Get, UsePipes, ValidationPipe } from '@nestjs/common';
import { IsNumber } from 'class-validator';
import { AppService } from './app.service';

const MyField = createParamDecorator((data, req) => {
  const result = new TestDto();
  result.field1 = Number(req.query.field1);
  return result;
});

class TestDto {
  @IsNumber()
  field1: number;
}

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {
  }

  @Get()
  @UsePipes(new ValidationPipe({ transform: true }))
  getHello(@MyField() testDto: TestDto): TestDto {
    return testDto;
  }
}
like image 160
kamilg Avatar answered Nov 16 '22 14:11

kamilg


Another option is to enable implicit conversion.

@UsePipes(new ValidationPipe({ transform: true, transformOptions: { enableImplicitConversion: true } }))
like image 32
Migi Avatar answered Nov 16 '22 13:11

Migi


You can use app.useGlobalPipes(new ValidationPipe({ transform: true })); on main.ts too. It's work for me 😊

like image 29
Marluan Espiritusanto Guerrero Avatar answered Nov 16 '22 13:11

Marluan Espiritusanto Guerrero