mcpField
C
class-validator

class-validator

github.com/claude-dev-suite開発4645 回インストール2 日前に更新
リポジトリ

class-validator について

TypeScriptのバックエンド開発者向けclass-validatorリファレンススキルです。デコレーターによるDTO検証、NestJSとの連携、設定時の一般的な問題を扱います。

主な機能

  1. 01文字列・数値・日付の検証
  2. 02ネストしたオブジェクトと配列の検証
  3. 03NestJS ValidationPipeとの連携
  4. 04カスタムバリデーターの基本例
  5. 05デコレーター設定とトラブル対応

活用シーン

  1. 01APIリクエストDTOの検証
  2. 02NestJSでの未知フィールドの除外
  3. 03ネストしたオブジェクトや配列の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill class-validator

インストール後、Claude Code で/class-validatorを入力して読み込まれたか確認します。

同梱ファイルadvanced.md

詳細

Class-validator - クイックリファレンス

完全なリファレンス:カスタムバリデーター、フィールド間のバリデーション、バリデーショングループ、エラーのフォーマット、class-transformer との統合については、advanced.md を参照してください。

詳細情報:包括的なドキュメントを確認するには、technology: class-validator を指定して mcp__documentation__fetch_docs を使用してください。

セットアップ

npm install class-validator class-transformer
// tsconfig.json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

基本的な使い方

import {
  validate,
  IsString,
  IsEmail,
  IsInt,
  Min,
  Max,
  Length,
  IsOptional,
} from 'class-validator';
import { plainToInstance } from 'class-transformer';

class CreateUserDto {
  @IsString()
  @Length(2, 50)
  name: string;

  @IsEmail()
  email: string;

  @IsInt()
  @Min(18)
  @Max(120)
  age: number;

  @IsOptional()
  @IsString()
  bio?: string;
}

// Validate
async function validateUser(data: unknown) {
  const user = plainToInstance(CreateUserDto, data);
  const errors = await validate(user);

  if (errors.length > 0) {
    throw new Error(errors.map((e) => Object.values(e.constraints || {})).flat().join(', '));
  }

  return user;
}

よく使うデコレーター

文字列バリデーター
import {
  IsString, IsNotEmpty, Length, MinLength, MaxLength,
  Matches, IsUUID, IsEmail, IsUrl, IsIP,
} from 'class-validator';

class StringValidationDto {
  @IsString()
  @IsNotEmpty()
  required: string;

  @Length(5, 20)
  username: string;

  @MinLength(8)
  @Matches(/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/, {
    message: 'Password must contain letters and numbers',
  })
  password: string;

  @IsEmail()
  email: string;

  @IsUrl({ require_protocol: true })
  website: string;

  @IsUUID('4')
  id: string;
}
数値バリデーター
import { IsNumber, IsInt, IsPositive, Min, Max, IsDivisibleBy } from 'class-validator';

class NumberValidationDto {
  @IsNumber()
  decimal: number;

  @IsInt()
  integer: number;

  @IsPositive()
  positive: number;

  @Min(0)
  @Max(100)
  percentage: number;
}
日付バリデーター
import { IsDate, MinDate, MaxDate, IsISO8601 } from 'class-validator';
import { Type } from 'class-transformer';

class DateValidationDto {
  @IsDate()
  @Type(() => Date)
  date: Date;

  @MinDate(new Date('2020-01-01'))
  @Type(() => Date)
  afterDate: Date;

  @IsISO8601()
  isoString: string;
}
配列バリデーター
import {
  IsArray, ArrayMinSize, ArrayMaxSize, ArrayUnique,
  ArrayNotEmpty, ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';

class ItemDto {
  @IsString()
  name: string;
}

class ArrayValidationDto {
  @IsArray()
  @ArrayNotEmpty()
  @ArrayMinSize(1)
  @ArrayMaxSize(10)
  @IsString({ each: true })  // Validate each item
  tags: string[];

  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => ItemDto)
  items: ItemDto[];
}
ネストしたオブジェクトのバリデーション
import { IsObject, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';

class AddressDto {
  @IsString()
  street: string;

  @IsString()
  city: string;
}

class UserWithAddressDto {
  @IsString()
  name: string;

  @ValidateNested()
  @Type(() => AddressDto)
  address: AddressDto;
}
その他のバリデーター
import {
  IsBoolean, IsEnum, IsIn, IsOptional,
  IsCreditCard, IsPhoneNumber,
} from 'class-validator';

enum UserRole {
  ADMIN = 'admin',
  USER = 'user',
  GUEST = 'guest',
}

class MixedValidationDto {
  @IsBoolean()
  active: boolean;

  @IsEnum(UserRole)
  role: UserRole;

  @IsIn(['draft', 'published', 'archived'])
  status: string;

  @IsOptional()
  @IsCreditCard()
  creditCard?: string;
}

NestJS との統合

// main.ts
import { ValidationPipe } from '@nestjs/common';

app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,           // Strip non-decorated properties
    forbidNonWhitelisted: true, // Throw on unknown properties
    transform: true,            // Auto-transform payloads to DTO instances
    transformOptions: {
      enableImplicitConversion: true,
    },
  })
);
// users.controller.ts
@Controller('users')
export class UsersController {
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    // createUserDto is already validated and transformed
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll(@Query() query: PaginationQueryDto) {
    // Query params also validated
    return this.usersService.findAll(query);
  }

  @Get(':id')
  findOne(@Param('id', ParseUUIDPipe) id: string) {
    return this.usersService.findOne(id);
  }
}
部分 DTO(更新用)
import { PartialType, OmitType, PickType, IntersectionType } from '@nestjs/mapped-types';

// All fields optional
export class UpdateUserDto extends PartialType(CreateUserDto) {}

// Omit specific fields
export class CreateUserWithoutPasswordDto extends OmitType(CreateUserDto, ['password']) {}

// Pick specific fields
export class LoginDto extends PickType(CreateUserDto, ['email', 'password']) {}

// Combine DTOs
export class ExtendedUserDto extends IntersectionType(CreateUserDto, AdditionalFieldsDto) {}

カスタムバリデーター(基本)

import {
  registerDecorator,
  ValidationOptions,
  ValidatorConstraint,
  ValidatorConstraintInterface,
  ValidationArguments,
} from 'class-validator';

@ValidatorConstraint({ async: false })
export class IsStrongPasswordConstraint implements ValidatorConstraintInterface {
  validate(password: string, args: ValidationArguments) {
    const hasUppercase = /[A-Z]/.test(password);
    const hasLowercase = /[a-z]/.test(password);
    const hasNumber = /\d/.test(password);
    const minLength = password.length >= 8;

    return hasUppercase && hasLowercase && hasNumber && minLength;
  }

  defaultMessage(args: ValidationArguments) {
    return 'Password must contain uppercase, lowercase, and number';
  }
}

export function IsStrongPassword(validationOptions?: ValidationOptions) {
  return function (object: Object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [],
      validator: IsStrongPasswordConstraint,
    });
  };
}

// Usage
class RegisterDto {
  @IsStrongPassword()
  password: string;
}

このスキルを使用しない場合

  • クライアント側のバリデーション - React Hook Form と Zod を使用します
  • NestJS 以外のバックエンド - Zod のほうがフレームワークに依存しません
  • 関数型プログラミングのパターン - デコレーターにはクラスが必要です
  • シンプルなバリデーション - Zod のほうが API がシンプルです

比較:class-validator と Zod

機能class-validatorZod
スタイルデコレーター関数型
フレームワークNestJS の標準フレームワーク非依存
変換class-transformer組み込み
バンドルサイズ大きい小さい
型推論手動自動

アンチパターン

アンチパターン問題点正しい方法
@Type() デコレーターがないネストしたバリデーションが失敗しますネストしたオブジェクトには @Type() を使用します
ValidationPipe で whitelist を設定しない未知のプロパティを受け入れますwhitelist: true を設定します
DTO で any を使用するバリデーションを回避します適切な型を使用します
コントローラーで手動バリデーションを行うコードが重複しますValidationPipe をグローバルに使用します
experimentalDecorators がないデコレーターが動作しませんtsconfig.json で有効にします
class-transformer を使用しないプレーンオブジェクトがバリデーションされませんplainToInstance() を使用します

よくある問題の解決

問題原因解決策
デコレーターが動作しないtsconfig の設定ミスexperimentalDecorators: true を有効にします
ネストしたバリデーションが失敗する@Type() がない@Type(() => NestedClass) を追加します
バリデーションが実行されないValidationPipe が設定されていないmain.ts で ValidationPipe をグローバルに追加します
未知のプロパティが受け入れられるwhitelist が設定されていないValidationPipe で whitelist: true を設定します
変換が動作しないtransform オプションがないValidationPipe で transform: true を設定します
非同期バリデーターが失敗する設定が誤っている@ValidatorConstraint で async: true を使用します

チェックリスト

  • デコレーターを有効にした tsconfig
  • ネスト/変換用の class-transformer
  • NestJS でのグローバル ValidationPipe
  • whitelist と forbidNonWhitelisted
  • 特定のロジック用のカスタムバリデーター
  • API レスポンス用のエラーフォーマット

参考資料

詳細情報:technology: class-validator を指定して mcp__documentation__fetch_docs を使用してください

コメント