class-validator
class-validator copied to clipboard
question: accept only objects of a given validate-required form within an array
I was trying to... Allow only an array of objects to be passed on a given property
The problem:
ValidateNested
allows both objects and arrays, making it possible to pass any amounts of nested arrays as long as in any moment where there's any other type defined within the matrixes it is according to the other constrains.
@ArrayMinSize(0)
@ArrayMaxSize(5)
@ValidateNested({ each: true })
@Type(() => OfferPricingDTO)
pricing: OfferPricingDTO[];
// allows [[[]]], where my interest is to only allow objects with the passed type as direct items of this property.
I am also struggling with this issue, please look into it.
Just came across this issue. Also noticed that while validating objects (not within an array) it accepts an empty array successfully.
Related stackoverflow: https://stackoverflow.com/questions/73527819/nestjs-validate-an-array-of-objects
Same issue here...
If I understand this correctly, you could end up with a lot of different objects (or empty arrays) in your array, and you want to make sure that they are validated as an OfferPricingDTO
?
Can you show us the definition and validation decorators for the OfferPricingDTO
class?
I think a possible solution here is to use an abstract Validation class such as this:
import { plainToInstance } from 'class-transformer';
import { ValidationError, validate, validateOrReject } from 'class-validator';
export type StaticThis<T> = { new (): T };
export abstract class Validation {
static async check<T extends Validation>(
this: StaticThis<T>,
data: T,
validate = true,
): Promise<T> {
const that = plainToInstance(this, data);
if (validate) {
await validateOrReject(that, {
skipMissingProperties: true,
});
}
return that;
}
static validate<T extends Validation>(
this: StaticThis<T>,
data: T,
reject = true,
): Promise<void | ValidationError[]> {
const that = plainToInstance(this, data);
if (reject) {
return validateOrReject(that, {
skipMissingProperties: true,
});
}
return validate(that);
}
}
You can then extend this class in your DTO, which lets you use OfferPricingDTO.validate(someObject)
or OfferPricingDTO.check(someObject)
to validate an OfferPricingDTO
:
export class OfferPricingDTO extends Validation {
@IsString()
myProp: string
}
This lets you validate your array of OfferPricingDTO
using @Validate
like so:
export class MyClass {
@ArrayMinSize(0)
@ArrayMaxSize(5)
@Validate(OfferPricingDTO.validate, { each: true })
@Type(() => OfferPricingDTO)
pricing: OfferPricingDTO[];
}
I'm sure there is a way to achieve this without using the Validation
class I have described here, but I use that in all my class-validator
projects since I find it very convenient. It all depends on how you construct your objects and such, and if you use class-transformer
or not.
Anyways, the @Validate
decorator is your friend here.
I still think there is a legitimate issue here. It doesn't seem intuitive or practical that ValidateNested
accepts empty arrays.
I still think there is a legitimate issue here. It doesn't seem intuitive or practical that
ValidateNested
accepts empty arrays.
I totally agree on this. A custom validation is a hotfix for this issue however the current implementation / interface is misunderstanding for developers. And it can get abused in production versions because you can pass the validation layer.
I think the issue is twofold:
- It's not nice that empty arrays are accepted by
ValidateNested
. There should at least be an option to disable that behaviour or something. - It would be nice to have a simple way to tell
class-validator
to validate an object property of a specific class, similar to@IsEnum(entity: object)
. For example, it could look like this:@ValidateNested(OfferPricingDTO, {each: true})
I hope that @NoNameProvided will look at it, because that should be fixed.
Is it already possible to do this easily ? Default behavior still allows empty Arrays and arrays of arrays.