Nested dto with multipart form data

432 views Asked by At

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

1

There are 1 answers

0
miliereya On

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
}