I'm trying to find a nice way to validate a body using DTO (using the brilliant class-validator
and class-transformer
libraries). It works really well, even for nested structures but in my case I'd like to have the body property based on some conditions.
Example that will probably help to understand:
Let's imagine my body should always have selectedCategory
.
Based on that field, the content could either be from category 1, which contains prop1
OR from category 2, which contains prop2
.
I do not want to allow a null for both of them, I really want to have to either have prop1
defined or prop2
based on the selectedCategory
.
I think that I could use a pipe, but then how can I specify the correct DTO to use?
I've built a "base" class with all the common properties and few other classes that inherit from it.
I could instantiate the pipe manually based on the property selectedCategory
, that'd be ideal but I have no clue what to pass as a second argument of the pipe (metadata).
Thanks for your help.
Have you tried using groups? Instead of having multiple DTOs, you just create one DTO. Every property is assigned to one or multiple groups:
@Min(12, {groups: ['registration', 'update']})
age: number;
@Length(2, 20, {groups: ['registration']})
name: string;
You can then conditionally pass the groups to class transformer / validator:
@Injectable()
export class ConditionalValidationPipe implements PipeTransform {
async transform(entity: any, metadata: ArgumentMetadata) {
// Dynamically determine the groups
const groups = [];
if (entity.selectedCategory === 1) {
groups.push('registration');
}
// Transform to class with groups
const entityClass = plainToClass(EntityDto, entity, { groups })
// Validate with groups
const errors = await validate(entityClass, { groups });
if (errors.length > 0) {
throw this.createError(errors);
}
return entityClass;
}
}