Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class validator with array of nested objects in nestjs

I'm trying to validate array of nested objects in nestjs and it works fine. But if I send non-array value like string or null, then I get an error:

TypeError: Cannot read property 'length' of undefined.

Nested field:

@IsArray()
@ValidateNested({ each: true })
@Type(() => NestedDTO)
nested: NestedDTO[];

How can I validate that value is an array before validate nested objects?

Versions:

NestJS: 6.10.14
class-validator: 0.11.0
like image 496
Oleg Vakulenko Avatar asked Feb 21 '20 08:02

Oleg Vakulenko


Video Answer


1 Answers

Out-of-the-box support for validation of array of objects is kind of scarce in NestJS so far.

Please have at look at my solution:

The main point is to write and then to use your custom IsArrayOfObjects decorator that will contain the validation logic + @Type decorator from class-transformer module.

import { Type } from 'class-transformer';
import {
  IsString,
  registerDecorator,
  ValidateNested,
  ValidationArguments,
  ValidationOptions,
} from 'class-validator';

export function IsArrayOfObjects(validationOptions?: ValidationOptions) {
  return (object: unknown, propertyName: string) => {
    registerDecorator({
      name: 'IsArrayOfObjects',
      target: object.constructor,
      propertyName,
      constraints: [],
      options: validationOptions,
      validator: {
        validate(value: any): boolean {
          return (
            Array.isArray(value) &&
            value.every(
              (element: any) =>
                element instanceof Object && !(element instanceof Array),
            )
          );
        },
        defaultMessage: (validationArguments?: ValidationArguments): string =>
          `${validationArguments.property} must be an array of objects`,
      },
    });
  };
}

Suppose we have this NestedDTO:

export class NestedDTO {
  @IsString()
  someProperty: string;
}

Then we can simply use it for defining like:

@IsArrayOfObjects()
@ValidateNested()
@Type(() => NestedDTO)
nested: NestedDTO[];

However, this solution will allow empty array to be passed. Then you can add extra condition in IsArrayOfObjects like value.length > 0 and also correct its defaultMessage.

like image 79
Hlib Derbenov Avatar answered Sep 30 '22 18:09

Hlib Derbenov