I need to use a nested Dto in nest-js project
import { Type } from 'class-transformer'
import {
IsArray,
IsNotEmpty,
IsString,
MaxLength,
ArrayMaxSize,
ValidateNested,
} from 'class-validator'
class CreateHighlightBody {
@IsString()
@MaxLength(255)
title: string
@IsArray()
@ArrayMaxSize(10)
content: string[]
@IsNotEmpty()
@IsString()
game: string
}
export class CreateHighlightDto {
@ValidateNested()
@Type(() => CreateHighlightBody)
body: CreateHighlightBody
}
It works fine with common JSON body in request, but if I want to use Multipart Form Data it will give the following error:
{
"message": [
"nested property body must be either object or array"
],
"error": "Bad Request",
"statusCode": 400
}
My controller:
@Post('create')
@Auth()
@UseInterceptors(FileInterceptor('file'))
async create(
@CurrentUser() user: UserCurrent,
@Body() dto: CreateHighlightDto,
@UploadedFile(new ParseFilePipe())
file: Express.Multer.File
) {
return this.highlightService.createHighlight(user, file, dto.body)
}
I use insomnia to test API
The solution was to create a pipe to parse the body correctly.
type TParseFormDataJsonOptions = {
field: string
}
export class ParseFormDataJsonPipe implements PipeTransform {
constructor(private options?: TParseFormDataJsonOptions) {}
transform(value: any) {
const { field } = this.options
const jsonField = value[field].replace(
/(\w+:)|(\w+ :)/g,
function (matchedStr: string) {
return (
'"' + matchedStr.substring(0, matchedStr.length - 1) + '":'
)
}
)
return JSON.parse(jsonField)
}
}
Controller:
@Post('create')
@Auth()
@UseInterceptors(FileInterceptor('file'))
async create(
@UploadedFile(new ParseFilePipe({}))
file: Express.Multer.File,
@Body(
new ParseFormDataJsonPipe({ field: 'body' }),
new ValidationPipe()
)
dto: CreateHighlightDto,
@CurrentUser()
user: UserCurrent
) {
console.log(dto)
}
Also changed Dto to:
export class CreateHighlightDto {
@IsString()
@MaxLength(255)
title: string
@IsArray()
@ArrayMaxSize(10)
content: string[]
@IsNotEmpty()
@IsString()
game: string
}