ん〜、class-validatorでバリデーションしよかあ

# class-validatorとは
nodeで使うことができる入力値バリデーション用のライブラリのこと。
これを使うことで簡単にバリデーションを実装することが可能。

# インストール方法

```bash
npm install class-validator --save
```

# 使い方
## 基本的なバリデーション

```typescript
import {
  IsString,
  IsNotEmpty,
  Length,
  IsNumber,
  IsInt,
  Min,
  Max,
  IsBoolean,
  IsArray,
  ArrayNotEmpty,
  IsDate,
  IsOptional,
  IsEmail
} from 'class-validator';

export class CreateUserDto {
  // 文字列の検証
  @IsString({ message: '名前は文字列である必要があります' })
  @IsNotEmpty({ message: '名前は空であってはいけません' })
  @Length(3, 20, { message: '名前は3文字以上20文字以下である必要があります' })
  name: string;

  // 数値の検証
  @IsNumber({}, { message: '価格は数値である必要があります' })
  @Min(0.01, { message: '価格は0.01以上である必要があります' })
  price: number;

  // 整数の検証
  @IsInt({ message: '年齢は整数である必要があります' })
  @Min(0, { message: '年齢は0以上である必要があります' })
  @Max(150, { message: '年齢は150以下である必要があります' })
  age: number;

  // ブール値の検証
  @IsBoolean({ message: 'アクティブは真偽値である必要があります' })
  isActive: boolean;

  // 配列の検証
  @IsArray({ message: 'タグは配列である必要があります' })
  @ArrayNotEmpty({ message: 'タグは空の配列であってはいけません' })
  tags: string[];

  // 日付の検証
  @IsDate({ message: '作成日は有効な日付である必要があります' })
  @IsOptional() // このフィールドはオプションです
  createdDate?: Date;

  // メールアドレスの検証
  @IsEmail({}, { message: '有効なメールアドレスを入力してください' })
  email: string;

  // オプションの文字列フィールドの検証
  @IsString({ message: '備考は文字列である必要があります' })
  @IsOptional() // このフィールドはオプションです
  remarks?: string;
}

```

## カスタムバリデーション

```typescript
import {
  registerDecorator,          // デコレーターを登録するための関数をインポート
  ValidationOptions,           // バリデーションオプションをインポート
  ValidatorConstraint,         // バリデーター制約を定義するためのデコレーターをインポート
  ValidatorConstraintInterface,// バリデーター制約インターフェースをインポート
  ValidationArguments,         // バリデーション時の引数をインポート
} from 'class-validator';

// バリデーター制約の実装クラスを定義し、非同期ではないことを示す
@ValidatorConstraint({ async: false })
export class IsPasswordStrongConstraint implements ValidatorConstraintInterface {
  
  // パスワードのバリデーションロジックを定義する関数
  validate(password: string, args: ValidationArguments) {
    // パスワードが少なくとも1つの数字を含むかどうかをチェック
    const hasNumber = /[0-9]/.test(password);
    // パスワードが少なくとも1つの特殊文字を含むかどうかをチェック
    const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
    // 数字と特殊文字の両方を含んでいる場合にtrueを返す
    return hasNumber && hasSpecialChar;
  }

  // デフォルトのエラーメッセージを定義する関数
  defaultMessage(args: ValidationArguments) {
    // カスタムエラーメッセージを返す
    return 'パスワードは少なくとも1つの数字と1つの特殊文字を含む必要があります';
  }
}

// デコレーター関数を定義する
export function IsPasswordStrong() {
  return function (object: Object, propertyName: string) {
    // デコレーターを登録する
    registerDecorator({
      target: object.constructor, // デコレーターが適用されるクラスを指定
      propertyName: propertyName, // デコレーターが適用されるプロパティを指定
      constraints: [],            // 制約を指定（ここでは特に無し）
      validator: IsPasswordStrongConstraint, // 使用するバリデータークラスを指定
    });
  };
}

```

カスタムバリデーターをimportして設定することで使うことができる。

```typescript
import { IsString, Length } from 'class-validator';
import { IsPasswordStrong } from './is-password-strong.validator';

export class CreateUserDto {
  @IsString({ message: 'パスワードは文字列である必要があります' })
  @Length(6, 20, { message: 'パスワードは6文字以上20文字以下である必要があります' })
  @IsPasswordStrong() // カスタムバリデーションを設定
  password: string;
}
```

## エラーが発生した場合のレスポンス例

```typescript
[
    {
      "target": {
        "id": "abcdefghi",
        "name": "やまだたろう",
        "department": 2
      },
      "value": "abcdefghi",
      "property": "id",
      "children": [],
      "constraints": {
        "isNumberString": "id must be a number string",
        "length": "5〜6桁で入力してください"
      }
    }
  ]
```

# 参考
[class-validator](https://github.com/typestack/class-validator)
[npmライブラリ・class-validatorでバリデーションをする](https://qiita.com/t-kubodera/items/2839ec4e4fe667b43f18)
[class-validatorをカスタムする](https://www.greenwich.co.jp/blog-archives/p/29504)
[class-validatorで、他のプロパティの値を参照して一致しているかチェックするCustomValidator](https://qiita.com/SuyamaDaichi/items/f3c0cbdf0fdf4e6257ac)
[【NestJS】入力値のバリデーションを簡単に実装する](https://popy1017.hatenablog.com/?page=1675570960)
[【NestJS】class-validatorのメッセージを日本語化する（プロパティ名自動置換）](https://zenn.dev/gnz/articles/c50f2a3f926614)