Dynamically pass country code to @IsPhoneNumber() based on user input

Viewed 586

I have a DTO which utilizes class validators as follows.

export class RandomlyNamedDto {
  @IsRequired()
  @Max(3)
  @Min(2)
  countryCode: string;
  
  @IsPhoneNumber()
  phone: string;
}

I want to pass the country code to @IsPhoneNumber() based on the country code sent by the user. For example if countryCode = 'UK' the validator will be like follows.

IsPhoneNumber('UK')

1 Answers

You can achieve this by creating a custom decorator and registering it with the class-validator library. The code below is adapted from another stack overflow answer to fit your criteria:

import {
  ValidationOptions,
  registerDecorator,
  ValidationArguments,
  buildMessage,
  IsDefined,
  validate,
  isPhoneNumber,
  MaxLength,
  MinLength,
} from "class-validator";

export function IsPhoneNumberForRegion(
  property: string,
  validationOptions?: ValidationOptions
) {
  return function (object: Object, propertyName: string) {
      registerDecorator({
        name: "isPhoneNumberForRegion",
        target: object.constructor,
        propertyName: propertyName,
        constraints: [property],
        options: validationOptions,
        validator: {
            validate(value: any, args: ValidationArguments) {
              // the property name that holds the country code
              const [countryCodeField] = args.constraints;

              // the value of the country code on the target object
              const countryCode = (args.object as any)[countryCodeField];

              // validate phone number for specified region
              return isPhoneNumber(value, countryCode);
            },
            // specify custom error message
            defaultMessage: buildMessage(
            (eachPrefix) =>
              `${eachPrefix} $property must be a valid phone number in the specified region`,
            validationOptions
            ),
        },
    });
  };
}

class ExampleUsage {
  @IsDefined()
  @MaxLength(3)
  @MinLength(2)
  countryCode: string;

  @IsPhoneNumberForRegion("countryCode")
  phone: string;

  constructor(countryCode: string, phone: string) {
    this.phone = phone;
    this.countryCode = countryCode;
  }
}

(async () => {
  const exampleGood = new ExampleUsage("US", "(201)-867-5309");
  const validationPasses = await validate(exampleGood);
  console.log(validationPasses);

  const exampleErrors = new ExampleUsage("US", "not a phone number");
  const validationWithErrors = await validate(exampleErrors);
  console.log(validationWithErrors);
})();
Related