How to validate an array of objects in nestjs using dto
Asked Answered
B

3

10

I am trying to validate an array of objects using DTO in nestjs. I have tried it but the data is not getting validated. I tried to search a lot but didn't get any answer. These are my files:

trivia.controller.ts file

import { Controller, Post, Body, ParseArrayPipe } from '@nestjs/common';
import { LoggerService } from '../logger/logger.service';
import { TriviaService } from './trivia.service';
import { PostTriviaScoreDto } from './dto/post-trivia-score.dto';

@Controller('trivia')
export class TriviaController {
  constructor(private readonly logger: LoggerService, private readonly triviaService: TriviaService) {}

    @Post('score')
    postTriviaScore(@Body(new ParseArrayPipe({ items: PostTriviaScoreDto })) postTriviaScoreDto: PostTriviaScoreDto) {
        this.logger.info('Trivia Controller : postTriviaScore : start');
        return this.triviaService.postTriviaScore(postTriviaScoreParamsDto, postTriviaScoreDto);
    }
}

trivia.service.ts file

import { LoggerService } from '../logger/logger.service';
import { PostTriviaScoreDto } from './dto/post-trivia-score.dto';

 @Injectable()
export class TriviaService {
  constructor(
    private readonly logger: LoggerService,
  ) {}
    postTriviaScore(allPlayersTriviaScore: PostTriviaScoreDto) {
            this.logger.info('Trivia Service : postTriviaScore : start');
            console.log('allPlayersScore ', allPlayersTriviaScore);
    
    }
}

post-trivia-score.dto.ts file

import { Type } from 'class-transformer';
import { IsNotEmpty, IsUUID, ValidateNested } from 'class-validator';

class TriviaScore {
  @IsNotEmpty()
  @IsUUID()
  question_id: string;

  @IsNotEmpty()
  @IsUUID()
  selected_option_id: string;

  @IsNotEmpty()
  answered_in_time: number;
}

export class PostTriviaScoreDto {
  @ValidateNested({ each: true })
  @Type(() => TriviaScore)
  items: TriviaScore[];
}

Structure of my JSON

[
  {
    "question_id": "088f1344-061e-4bcc-966f-512775f1f082",
    "selected_option_id": "72305e08-fedd-49b1-adb9-1dd92c88f4db",
    "answered_in_time": 2
  }
]

The properties are not getting validated here. Even if I pass a string in answered_in_time field, it accepts the body and doesn't throw an error or if I pass empty string in any of the fields then also it accepts the request body.

Please help me if you guys know the solution as I am really stuck here.

Bread answered 21/5, 2021 at 5:40 Comment(0)
F
4

I have also faced such problem and after googling I have fount that using

new ParseArrayPipe({items: YourDto, whitelist: true})

in controller. You also did this, you said body would be array but your dto is not array actually

postTriviaScoreDto: PostTriviaScoreDto // --> mistake

If you did:

postTriviaScoreDto: PostTriviaScoreDto[] // this will solve your problem
 

Information is taken from this website

Fujio answered 24/8, 2022 at 6:27 Comment(1)
This is also documented in Nestjs's official doc.Andino
E
3

I think the nested class there is not initialised and use it for type reference only so class-transformer does not know how to convert it to class instance.

set enableImplicitConversion: true can request class-transformer to convert it implicitly

main.ts

import { ValidationPipe } from '@nestjs/common';
app.useGlobalPipes(
    new ValidationPipe({
      transformOptions: {
        enableImplicitConversion: true, // allow conversion underneath
      },
    }),
);

After that, the nested properties should be able to be validated.


Some said implicit conversion may have problem on other cases but I cannot find a clean solution if class is not instantiated:

Why should we NOT use enableImplicitConversion when using class-transformer?

Esqueda answered 21/5, 2021 at 9:1 Comment(1)
Is there a way to set it for only one property? So that if there are any issues, they would be limited to that specific property and not affect all the system.Emmerich
H
0

The answer that works, to be clear for others, was posted by Kyung Lee in one of the comments and comes from the official NestJS documentation here

The code in particular that needs to be implemented is the ParseArrayPipe which specifies the type of object located in the DTO Array:

@Body(new ParseArrayPipe({ items: CreateUserDto }))
Handgrip answered 18/5 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.