Advanced conditions with class-validator (possible)?
Asked Answered
A

4

11

I have TypeScript NestJS project.

I need to validate incoming DTO to my API. It can be described as "creating of project" where we have type of building (House, Flat, Garden) and depending on that type we need to define:

  • House: FLOORS including ROOMS
  • Flat: ROOMS
  • Garden: nothing (it is one "room")

Example of house type:

{
  type: HOUSE,
  floors: [
    {
      name: "1st floor",
      rooms: [
        {
          name: "bedroom"
        }
      ]
    }
  ]
}

Example of flat type:

{
  type: FLAT,
  rooms: [
    {
      name: "bedroom"
    }
  ]
}

I've done this in past with help of AJV, but now as we migrated to NestJS, we started using class-validator.

My question is, if I can make those advanced conditionals (eg. when type is FLAT, then expect ROOMS only, but not FLOORS) in class-validator?

Alcaic answered 14/2, 2020 at 10:3 Comment(0)
A
6

With class-validator you have the options of Conditional Validation and of Group Validation, or you could always create a custom pipe and use AJV as you are used to. For the conditional validation you could create validations based on type and then let class-validator take care of the rest

Astronaut answered 14/2, 2020 at 16:30 Comment(3)
"you could create validations based on type", can you show an example?Inflexed
@Inflexed if I can figure out what I was meaning, then absolutely. I'm pretty sure I've seen examples somewhere, but I can't recall where at the momentAstronaut
This doesn't help at all. I'm also want to do the same thing. I only find basic examples which are all just copies from docs and nothing more advanced like the example in question. Saying there are "examples somewhere" doesn't say much as well.Wigging
T
2

You have to create a custom validator for that. The docs are pretty good: https://github.com/typestack/class-validator#custom-validation-classes

When you create your custom validator class, in your implementation of validate you can access the other parameter beeing validated in the args argument. Just write your if statements and return false if they are not met. You can even return your custom error messages and implement your own decorator.

Tongue answered 21/12, 2021 at 5:33 Comment(0)
E
1

I use this kind of validation for nasted objects:

export class PredefinedProductsDao {

  @IsEnum(ProductEnum)
  product: ProductEnum;

  @IsObject()
  @ValidateNested()
  @Type((type: TypeHelpOptions | undefined) => {
    if (type?.object) {
      const predefinedProductsDao: PredefinedProductsDao =
        type.object as PredefinedProductsDao;
      switch (predefinedProductsDao.editor) {
        case ProductEnum.FIRST:
          return FirstProductDao;
        case ProductEnum.SECOND:
          return SecondProductDao;
      }
    }
    return FirstProductDao;
  })
  predefinedConfig: FirstProductDao | SecondProductDao;
}

In my case I want validate predefinedConfig to be one of DAOs, different for each product. It's working as a shae

Evaluate answered 23/5, 2023 at 17:26 Comment(0)
A
0

I had similar requirements. If you're okay to nest the incoming object by one level, following pattern should work.

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

class MyRequestDto {
  @IsNotEmptyObject()
  @ValidateNested()
  @Type(null, {
    keepDiscriminatorProperty: true,
    discriminator: {
      property: 'type',
      subTypes: [
        {
          name: 'Flat',
          value: Flat,
        },
        {
          name: 'Room',
          value: Room,
        },
        {
          name: 'Garden',
          value: Garden,
        },
      ],
    },
  })
  entity: Flat | Room | Garden;
}

We can use the Type decorator provided by the class-transformer and pass in the discriminator option, which takes the property and subTypes attributes.

  • property is the propertyName to use for establishing the type of incoming of the incoming object.
  • subTypes will have the corresponding value (name) for the property and the class (value) to use for validation.

The interface definition can be found here

Arcograph answered 20/7, 2022 at 12:13 Comment(2)
TS2345: Argument of type 'null' is not assignable to parameter of type '((type?: TypeHelpOptions | undefined) => Function) | undefined'. How did it work for you?Sacristy
@Sacristy use () => Object insteadRibeiro

© 2022 - 2025 — McMap. All rights reserved.