Dev Study
← 解説「ValidationPipe — 入力の自動検証」に戻る

サンプルコードで身につける: ValidationPipe — 入力の自動検証

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1最小の検証 — 文字列チェック

@IsString() を1つ付けただけの最小の検証です。DTO のプロパティにルールを書き、main.ts でグローバルに ValidationPipe を有効化するという2点セットがすべての出発点になります。

import { IsString } from "class-validator";

export class CreateTagDto {
  @IsString() // 文字列でなければ自動で 400 Bad Request
  tagName: string;
}

// main.ts に1度だけ書く設定(全エンドポイントに効く):
// app.useGlobalPipes(new ValidationPipe());
//
// { "tagName": 123 } を送ると、ハンドラに届く前に
// { "statusCode": 400, "message": ["tagName must be a string"] }
// が自動で返る

2数値の範囲チェック

レビューの星評価のような数値項目に範囲ルールを重ねる例です。同じプロパティに付けたデコレータは「上から全部満たす」AND 条件として働き、違反内容はメッセージ配列として自動で返ります。

import { IsInt, Max, Min } from "class-validator";

export class CreateReviewDto {
  @IsInt() // 整数であること
  @Min(1)  // 1以上
  @Max(5)  // 5以下
  rating: number;
}

// 同じプロパティのデコレータは AND 条件になる
// { "rating": 9 } を送ると 400 と
// ["rating must not be greater than 5"] が返る

3任意項目と @IsOptional

「送られてきた場合だけ検証したい」更新系 DTO の定番パターンです。@IsOptional() を付けると未送信の項目は以降の検証がスキップされ、部分更新 API と検証ルールを両立できます。

import { IsOptional, IsString, MaxLength } from "class-validator";

export class UpdateProfileDto {
  @IsOptional() // 未送信ならこの項目の検証をスキップ
  @IsString()
  @MaxLength(30)
  displayName?: string;

  @IsOptional()
  @IsString()
  @MaxLength(200)
  bio?: string;
}
// 「送られた項目だけ検証して更新する」部分更新の定番 DTO

4検証の中身をプレーンTSで再現

ValidationPipe が裏でやっている「ルール一覧と実際の値を突き合わせ、違反メッセージを集める」処理をプレーン TypeScript で再現した例です。デコレータはこのルール登録を宣言的に書く手段にすぎないと分かります。

TypeScript

5会員登録DTOと whitelist

メール形式とパスワード長を検証する実務の会員登録 DTO です。whitelist: true を併用すると DTO に無いプロパティを自動で除去でき、isAdmin: true のような余計な項目を紛れ込ませる攻撃への基本的な防御になります。

import { IsEmail, IsString, MinLength } from "class-validator";

export class SignupDto {
  @IsEmail() // メールアドレスの形式チェック
  email: string;

  @IsString()
  @MinLength(8) // パスワードは8文字以上
  password: string;
}

// main.ts — 実務の定番設定:
// app.useGlobalPipes(new ValidationPipe({
//   whitelist: true, // DTO に無いプロパティを自動で除去する
// }));
//
// { "email": "...", "password": "...", "isAdmin": true }
// と送られても isAdmin は捨てられ、ハンドラには届かない