LoginSignup
2
1
お題は不問!Qiita Engineer Festa 2023で記事投稿!

class-validatorで、他のプロパティの値を参照して一致しているかチェックするCustomValidator

Posted at

ユースケース

例えばメールアドレスを使った新規登録画面

この場合、タイプミスなどで自分が意図していないパスワードが設定されてしまうのを防ぐために、二度同じパスワードを入力させるのが一般的です。
スクリーンショット 2023-07-01 0.43.23.png

バックエンドのリクエストバリデーションで使われがちなclass-validatorはRHFでも使える

class-validatorといえば、NestJSで使われるバリデーションですが、実はreact-hook-formのresolverに使うこともできます。

SignupPage.tsx
import { classValidatorResolver } from '@hookform/resolvers/class-validator';
import { RegisterUserRequestDTO } from '../DTO';
import { useForm } from 'react-hook-form';
import { classValidatorResolver } from '@hookform/resolvers/class-validator';

export const SignupPage = () => {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<RegisterUserRequestDTO>({ resolver: classValidatorResolver(RegisterUserRequestDTO) });

  // JSXは省略します
};

使い方のイメージ

IsEqual()というカスタムデコレーターを作ります。
呼び出す時はこんな感じで、第一引数に参照したいプロパティ名を指定してあげます。

RegisterUserRequestDTO.ts
import { IsEmail, IsNotEmpty, Matches, MinLength } from 'class-validator';
import { VALIDATION_ERRORS } from './error.constants';
import { isEqual } from './CustomValidator/isEqual';

export class RegisterUserRequestDTO {
  @IsEmail({}, { message: VALIDATION_ERRORS.INVALID_EMAIL })
  @IsNotEmpty({ message: VALIDATION_ERRORS.REQUIRED })
  email: string;

  @Matches(/[a-z]/, { message: VALIDATION_ERRORS.INVALID_PASSWORD })
  @Matches(/[A-Z]/, { message: VALIDATION_ERRORS.INVALID_PASSWORD })
  @Matches(/[0-9]/, { message: VALIDATION_ERRORS.INVALID_PASSWORD })
  @MinLength(8, { message: VALIDATION_ERRORS.INVALID_PASSWORD_LENGTH })
  @IsNotEmpty({ message: VALIDATION_ERRORS.REQUIRED })
  password: string;

  // 今回実装するのはこの `isEqual()`
  @isEqual('password', { message: VALIDATION_ERRORS.NOT_MATCH_PASSWORD })
  @IsNotEmpty({ message: VALIDATION_ERRORS.REQUIRED })
  passwordConfirm: string;
}

IsEqual()の実装

細かい説明は省きますがこんな感じ。公式ドキュメントを参照すれば誰でも作れますね。

CustmoValidator/isEqual.ts
import { ValidateBy, ValidationArguments, ValidationOptions } from 'class-validator';

export function isEqual(property: string, validationOptions?: ValidationOptions): PropertyDecorator {
  return ValidateBy(
    {
      name: 'isEqual',
      constraints: [property],
      validator: {
        validate(value, args: ValidationArguments): boolean {
          const [relatedPropertyName] = args.constraints;
          const relatedValue = (args.object as any)[relatedPropertyName];
          return relatedValue === value;
        },
      },
    },
    validationOptions || { message: `require to match with ${property}` },
  );
}

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1