so I was building an API where by user gives us type
and value
in the request body, type
could be CNIC, EMAIL, MOBILE
Now based on the type
I have to validate the value like if EMAIL
is valid or if MOBILE
is valid etc.
So as we can see that value
field depends on the type
field to validate it.
I need a way to handle this using class-validator
validationPipe
.
So at first I quickly opened the docs and started looking up for advance usage and I came to know that custom decorator would definitely help me and it did using bellow example.
validate-value-by-type.decorator.ts
import {
registerDecorator,
ValidationOptions,
ValidationArguments,
isEmail,
} from 'class-validator';
import { CA_DetailsTypes } from './models';
export function ValidateByAliasType(
property: string,
validationOptions?: ValidationOptions,
) {
// eslint-disable-next-line @typescript-eslint/ban-types
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'validateByAliasType',
target: object.constructor,
propertyName: propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
if (relatedValue === CA_DetailsTypes.EMAIL) {
return isEmail(value) && value.length > 5 && value.length <= 99;
}
if (relatedValue === CA_DetailsTypes.CNIC) {
return value.length === 13;
}
if (relatedValue === CA_DetailsTypes.MOBILE) {
return value.length === 11;
}
if (relatedValue === CA_DetailsTypes.TXT) {
return value.length > 3 && value.length <= 99;
}
return false;
},
},
});
};
}
client.request.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { IsEnum, IsNotEmpty } from 'class-validator';
export enum CA_DetailsTypes {
'CNIC' = 'CNIC',
'MOBILE' = 'MOBILE',
'EMAIL' = 'EMAIL',
'TXT' = 'TXT',
}
export class CA_FetchDetails_DTO {
@ApiProperty({
example: 'MOBILE',
type: 'enum',
enum: CA_DetailsTypes,
})
@IsNotEmpty()
@IsEnum(CA_DetailsTypes)
type: CA_DetailsTypes;
@ApiProperty({ example: '03070000002' })
@ValidateByAliasType('type')
value: string;
}
So with the above example I'm successfully able to validate value
by type
, but still I'm unable to customize the error message based on the type
ðŸ˜.
😢 So after a day of research I lose all my hope and decided to use another library for this complex validation and I did so but there was a drawback with that library and I ended up coming back to the class-validator
and after a lot of research I found a way to customize the error message based on the type
.
So here is the code for customizing error message based on the type
and I used defaultMessage()
to customize it.
validate-value-by-type.decorator.ts
import {
registerDecorator,
ValidationOptions,
ValidationArguments,
isEmail,
} from 'class-validator';
import { CA_DetailsTypes } from './models';
export function ValidateByAliasType(
property: string,
validationOptions?: ValidationOptions,
) {
// eslint-disable-next-line @typescript-eslint/ban-types
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'validateByAliasType',
target: object.constructor,
propertyName: propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
if (relatedValue === CA_DetailsTypes.EMAIL) {
return isEmail(value) && value.length > 5 && value.length <= 99;
}
if (relatedValue === CA_DetailsTypes.CNIC) {
return value.length === 13;
}
if (relatedValue === CA_DetailsTypes.MOBILE) {
return value.length === 11;
}
if (relatedValue === CA_DetailsTypes.TXT) {
return value.length > 3 && value.length <= 99;
}
return false;
},
defaultMessage(args?: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
switch (relatedValue) {
case CA_DetailsTypes.EMAIL:
return 'Please enter valid email!';
case CA_DetailsTypes.MOBILE:
return 'Please enter valid mobile!';
case CA_DetailsTypes.CNIC:
return 'Please enter valid CNIC!';
default:
return 'Invalid value!';
}
},
},
});
};
}
🤗 And finally it's done!