Reputation: 69
I'd like to do a following body validation in my NestJs app:
a
and b
are optional properties if one of them is supplied, otherwise one of them should be required
payload: { a: 'some value', c: 'another value' } -> OK
payload: { b: 'some value', c: 'another value' } -> OK
payload: { a: 'some value1', b: 'some value2', c: 'another value' } -> OK
payload: { c: 'another value' } -> Error: either a or b should be present in the payload`
I have the following DTO:
class MyDto {
@OneOfOptionalRequired(['a', 'b'])
@ApiProperty()
@IsString()
@IsOptional()
a: string
@OneOfOptionalRequired(['a', 'b'])
@ApiProperty()
@IsString()
@IsOptional()
b: string
@ApiProperty()
@IsString()
c: string
}
I've tried creating my own decorator which will perform described validation:
export function OneOfOptionalRequired(requiredFields: string[], validationOptions?: ValidationOptions) {
return function (target: object, propertyName: string) {
registerDecorator({
name: 'oneOfOptionalRequired',
target: target.constructor,
propertyName: propertyName,
options: {
message: `Missing one of the required fields [${requiredFields}]`,
...validationOptions
},
validator: {
validate(value: unknown, args: ValidationArguments) {
const payload = args.object
return requiredFields.some(x => has(payload, x))
},
},
})
}
}
But actually it does not work for the case when only c
property is present in the payload, because @IsOptional() turns off all of the decorators. If I remove @IsOptional() for a
and b
then I will get the error saying that a
and b
should not be empty. So I'm kind of stuck here
Upvotes: 2
Views: 8531
Reputation: 1
You can expand the ValidateIf
decorator to add when those two fields exists.
@ValidateIf(dto => !dto.b || (dto.a && dto.b))
@IsString()
a?: string;
@ValidateIf(dto => !dto.a || (dto.a && dto.b))
@IsString()
b?: string;
Upvotes: 0
Reputation: 505
You could make your own decorator, especially if this is a repeating pattern in your application or if you expect to need this replicated over more than two properties in the same DTO. However, for this specific case, you could solve it in a more straight-forward manner:
@ValidateIf(dto => typeof dto.b === 'undefined')
@IsString()
a?: string;
@ValidateIf(dto => typeof dto.a === 'undefined')
@IsString()
b?: string;
Of course, you can export and re-use this as following:
export const RequiredIfPropertyMissing = (property: string) =>
ValidateIf(dto: any => typeof dto[property] === 'undefined');
Hope it helps, and good luck!
Upvotes: 2