NestJSにおけるPipeは、リクエストデータの変換と検証を担う重要なコンポーネントです。適切なPipeを活用することで、Controllerに到達する前に不正なデータを排除し、型安全で堅牢なAPIを構築できます。本記事では、組み込みPipeの活用方法からclass-validatorを使ったDTO検証、さらにカスタムPipeの作成まで、実践的なバリデーション手法を解説します。
実行環境と前提条件#
本記事の内容を実践するにあたり、以下の環境を前提としています。
| 項目 |
バージョン・要件 |
| Node.js |
20以上 |
| npm |
10以上 |
| NestJS |
11.x |
| class-validator |
0.14.x |
| class-transformer |
0.5.x |
| OS |
Windows / macOS / Linux |
| エディタ |
VS Code(推奨) |
事前に以下の準備を完了してください。
- NestJS CLIのインストール済み
- NestJSプロジェクトの作成済み
- 基本的なController・Serviceの理解
NestJSプロジェクトの作成方法はNestJS入門記事、REST APIエンドポイントの実装はREST API開発記事を参照してください。
Pipeとは何か#
Pipeは、NestJSのリクエスト処理パイプラインにおいて、Controllerのルートハンドラが実行される直前に呼び出されるコンポーネントです。主に2つの役割を担います。
flowchart LR
A[クライアント] --> B[リクエスト]
B --> C[Middleware]
C --> D[Guard]
D --> E[Pipe]
E --> F[Controller]
F --> G[レスポンス]
G --> A
style E fill:#4CAF50,color:#fffPipeの2つの役割#
| 役割 |
説明 |
例 |
| 変換(Transformation) |
入力データを目的の形式に変換する |
文字列を数値に変換、日付文字列をDateオブジェクトに変換 |
| 検証(Validation) |
入力データを評価し、有効であればそのまま通過させ、無効であれば例外をスローする |
必須フィールドの存在確認、メールアドレス形式の検証 |
Pipeは例外ゾーン内で実行されるため、Pipe内でスローされた例外は例外フィルター(Exception Filter)で処理されます。つまり、Pipe内で例外が発生すると、Controllerのメソッドは実行されません。
組み込みPipeの活用#
NestJSは、一般的なユースケースに対応する組み込みPipeを@nestjs/commonパッケージで提供しています。
利用可能な組み込みPipe一覧#
| Pipe名 |
用途 |
変換元 → 変換先 |
ValidationPipe |
DTOベースのバリデーション |
オブジェクト → 検証済みオブジェクト |
ParseIntPipe |
整数への変換 |
文字列 → 整数 |
ParseFloatPipe |
浮動小数点への変換 |
文字列 → 浮動小数点数 |
ParseBoolPipe |
真偽値への変換 |
文字列 → boolean |
ParseArrayPipe |
配列への変換・検証 |
文字列 → 配列 |
ParseUUIDPipe |
UUID形式の検証 |
文字列 → UUID形式の文字列 |
ParseEnumPipe |
Enum値への変換 |
文字列 → Enum値 |
ParseDatePipe |
日付への変換 |
文字列 → Date |
DefaultValuePipe |
デフォルト値の設定 |
undefined/null → デフォルト値 |
ParseFilePipe |
ファイルの検証 |
ファイル → 検証済みファイル |
ParseIntPipeによる数値変換#
ルートパラメータやクエリパラメータはすべて文字列として受け取られます。ParseIntPipeを使用すると、自動的に整数に変換できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common';
import { ArticlesService } from './articles.service';
@Controller('articles')
export class ArticlesController {
constructor(private readonly articlesService: ArticlesService) {}
// GET /articles/123 → idは数値型として受け取る
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
console.log(typeof id); // 'number'
return this.articlesService.findOne(id);
}
// GET /articles?page=2&limit=10
@Get()
findAll(
@Query('page', ParseIntPipe) page: number,
@Query('limit', ParseIntPipe) limit: number,
) {
return this.articlesService.findAll({ page, limit });
}
}
|
不正な値(例:/articles/abc)を受け取った場合、NestJSは自動的に以下のエラーレスポンスを返します。
1
2
3
4
5
|
{
"statusCode": 400,
"message": "Validation failed (numeric string is expected)",
"error": "Bad Request"
}
|
ParseIntPipeのカスタマイズ#
エラー時のHTTPステータスコードをカスタマイズしたい場合は、インスタンスを直接渡します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import { Controller, Get, Param, ParseIntPipe, HttpStatus } from '@nestjs/common';
@Controller('articles')
export class ArticlesController {
@Get(':id')
findOne(
@Param('id', new ParseIntPipe({
errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE
}))
id: number,
) {
return this.articlesService.findOne(id);
}
}
|
ParseUUIDPipeによるUUID検証#
UUID形式のパラメータを検証する場合はParseUUIDPipeを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import { Controller, Get, Param, ParseUUIDPipe } from '@nestjs/common';
@Controller('users')
export class UsersController {
// UUID v4形式のみを許可
@Get(':uuid')
findOne(
@Param('uuid', new ParseUUIDPipe({ version: '4' }))
uuid: string,
) {
return this.usersService.findOne(uuid);
}
}
|
DefaultValuePipeによるデフォルト値設定#
オプショナルなクエリパラメータにデフォルト値を設定する場合は、DefaultValuePipeを他のPipeと組み合わせて使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import {
Controller,
Get,
Query,
DefaultValuePipe,
ParseIntPipe,
ParseBoolPipe
} from '@nestjs/common';
@Controller('articles')
export class ArticlesController {
// GET /articles?page=1&limit=10&published=true
// パラメータが省略された場合はデフォルト値を使用
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number,
@Query('published', new DefaultValuePipe(true), ParseBoolPipe) published: boolean,
) {
return this.articlesService.findAll({ page, limit, published });
}
}
|
class-validatorによるDTOバリデーション#
複雑なリクエストボディを検証する場合、class-validatorとclass-transformerを使用したDTOベースのバリデーションが効果的です。
パッケージのインストール#
1
|
npm install class-validator class-transformer
|
ValidationPipeのグローバル設定#
main.tsでValidationPipeをグローバルに設定することで、すべてのエンドポイントで自動的にバリデーションが適用されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // DTOに定義されていないプロパティを除去
forbidNonWhitelisted: true, // 未定義プロパティがあればエラー
transform: true, // 自動型変換を有効化
transformOptions: {
enableImplicitConversion: true, // 暗黙的な型変換を許可
},
}),
);
await app.listen(3000);
}
bootstrap();
|
ValidationPipeの主要オプション#
| オプション |
型 |
説明 |
whitelist |
boolean |
DTOに定義されていないプロパティを自動的に除去 |
forbidNonWhitelisted |
boolean |
未定義プロパティがあればエラーをスロー(whitelistと併用) |
transform |
boolean |
プレーンオブジェクトをDTOクラスのインスタンスに変換 |
disableErrorMessages |
boolean |
エラーメッセージの詳細を無効化(本番環境向け) |
skipMissingProperties |
boolean |
未定義プロパティの検証をスキップ(PATCH向け) |
forbidUnknownValues |
boolean |
不明な値の検証失敗(デフォルト: true) |
stopAtFirstError |
boolean |
最初のエラーで検証を停止 |
DTOクラスの定義#
class-validatorが提供するデコレータを使用して、バリデーションルールを定義します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
// src/articles/dto/create-article.dto.ts
import {
IsString,
IsNotEmpty,
IsOptional,
IsArray,
IsBoolean,
IsInt,
Min,
Max,
MinLength,
MaxLength,
IsEmail,
IsUrl,
ArrayMinSize,
ArrayMaxSize,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
export class AuthorDto {
@IsString()
@IsNotEmpty()
name: string;
@IsEmail()
email: string;
}
export class CreateArticleDto {
@IsString({ message: 'タイトルは文字列で入力してください' })
@IsNotEmpty({ message: 'タイトルは必須です' })
@MinLength(5, { message: 'タイトルは5文字以上で入力してください' })
@MaxLength(100, { message: 'タイトルは100文字以内で入力してください' })
title: string;
@IsString()
@IsNotEmpty()
@MinLength(50)
content: string;
@IsArray()
@ArrayMinSize(1, { message: '少なくとも1つのタグが必要です' })
@ArrayMaxSize(5, { message: 'タグは5つまでです' })
@IsString({ each: true }) // 配列の各要素を検証
tags: string[];
@IsOptional()
@IsBoolean()
published?: boolean;
@IsOptional()
@IsInt()
@Min(1)
@Max(100)
priority?: number;
@IsOptional()
@IsUrl()
thumbnailUrl?: string;
@ValidateNested() // ネストしたオブジェクトを検証
@Type(() => AuthorDto) // class-transformerで型変換
author: AuthorDto;
}
|
主要なバリデーションデコレータ一覧#
| カテゴリ |
デコレータ |
説明 |
| 存在検証 |
@IsDefined() |
undefined/null以外であること |
| 存在検証 |
@IsOptional() |
値がなければ他の検証をスキップ |
| 存在検証 |
@IsNotEmpty() |
空文字・null・undefined以外であること |
| 型検証 |
@IsString() |
文字列であること |
| 型検証 |
@IsNumber() |
数値であること |
| 型検証 |
@IsInt() |
整数であること |
| 型検証 |
@IsBoolean() |
真偽値であること |
| 型検証 |
@IsArray() |
配列であること |
| 型検証 |
@IsDate() |
Date型であること |
| 型検証 |
@IsEnum(enum) |
指定したEnumの値であること |
| 文字列検証 |
@MinLength(n) |
最小文字数 |
| 文字列検証 |
@MaxLength(n) |
最大文字数 |
| 文字列検証 |
@Length(min, max) |
文字数範囲 |
| 文字列検証 |
@Matches(regex) |
正規表現にマッチすること |
| 文字列検証 |
@IsEmail() |
メールアドレス形式 |
| 文字列検証 |
@IsUrl() |
URL形式 |
| 文字列検証 |
@IsUUID(version?) |
UUID形式 |
| 数値検証 |
@Min(n) |
最小値 |
| 数値検証 |
@Max(n) |
最大値 |
| 数値検証 |
@IsPositive() |
正の数 |
| 数値検証 |
@IsNegative() |
負の数 |
| 配列検証 |
@ArrayMinSize(n) |
最小要素数 |
| 配列検証 |
@ArrayMaxSize(n) |
最大要素数 |
| 配列検証 |
@ArrayNotEmpty() |
空でないこと |
| 配列検証 |
@ArrayUnique() |
重複要素がないこと |
| ネスト検証 |
@ValidateNested() |
ネストオブジェクトも検証 |
| 条件付き検証 |
@ValidateIf(condition) |
条件を満たす場合のみ検証 |
Controllerでの使用例#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// src/articles/articles.controller.ts
import { Controller, Post, Body, Put, Param, ParseIntPipe } from '@nestjs/common';
import { ArticlesService } from './articles.service';
import { CreateArticleDto } from './dto/create-article.dto';
import { UpdateArticleDto } from './dto/update-article.dto';
@Controller('articles')
export class ArticlesController {
constructor(private readonly articlesService: ArticlesService) {}
@Post()
create(@Body() createArticleDto: CreateArticleDto) {
// この時点でcreateArticleDtoは検証済み
return this.articlesService.create(createArticleDto);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateArticleDto: UpdateArticleDto,
) {
return this.articlesService.update(id, updateArticleDto);
}
}
|
エラーレスポンスの形式#
バリデーションエラーが発生すると、以下の形式でレスポンスが返されます。
1
2
3
4
5
6
7
8
9
|
{
"statusCode": 400,
"message": [
"タイトルは5文字以上で入力してください",
"少なくとも1つのタグが必要です",
"author.email must be an email"
],
"error": "Bad Request"
}
|
Mapped Typesによる更新DTOの作成#
NestJSの@nestjs/mapped-typesパッケージを使用すると、既存のDTOを元に派生DTOを効率的に作成できます。
パッケージのインストール#
1
|
npm install @nestjs/mapped-types
|
PartialTypeによる部分更新DTO#
すべてのプロパティをオプショナルにした更新用DTOを作成します。
1
2
3
4
5
|
// src/articles/dto/update-article.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateArticleDto } from './create-article.dto';
export class UpdateArticleDto extends PartialType(CreateArticleDto) {}
|
PartialTypeを使用すると、CreateArticleDtoのすべてのプロパティがオプショナルになり、PATCH操作に適したDTOが作成されます。
その他のMapped Types#
| ユーティリティ |
説明 |
使用例 |
PartialType(T) |
すべてのプロパティをオプショナルに |
更新DTO |
PickType(T, keys) |
指定したプロパティのみを抽出 |
特定フィールドのみ更新 |
OmitType(T, keys) |
指定したプロパティを除外 |
ID除外の作成DTO |
IntersectionType(A, B) |
2つの型を結合 |
複数DTOの統合 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import { PickType, OmitType, IntersectionType } from '@nestjs/mapped-types';
import { CreateArticleDto } from './create-article.dto';
// titleとcontentのみを持つDTO
export class UpdateTitleContentDto extends PickType(CreateArticleDto, [
'title',
'content',
] as const) {}
// authorを除いたDTO
export class CreateArticleWithoutAuthorDto extends OmitType(CreateArticleDto, [
'author',
] as const) {}
// 複数型の結合
class AdditionalFields {
@IsString()
externalId: string;
}
export class ExtendedArticleDto extends IntersectionType(
CreateArticleDto,
AdditionalFields,
) {}
|
配列のバリデーション#
配列形式のリクエストボディを検証する場合、ParseArrayPipeを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import { Controller, Post, Body, ParseArrayPipe } from '@nestjs/common';
import { CreateArticleDto } from './dto/create-article.dto';
@Controller('articles')
export class ArticlesController {
// POST /articles/bulk - 複数記事の一括作成
@Post('bulk')
createBulk(
@Body(new ParseArrayPipe({ items: CreateArticleDto }))
createArticleDtos: CreateArticleDto[],
) {
return this.articlesService.createMany(createArticleDtos);
}
}
|
クエリパラメータのカンマ区切り配列を解析する場合も同様です。
1
2
3
4
5
6
7
8
9
10
11
|
@Controller('articles')
export class ArticlesController {
// GET /articles?ids=1,2,3,4,5
@Get()
findByIds(
@Query('ids', new ParseArrayPipe({ items: Number, separator: ',' }))
ids: number[],
) {
return this.articlesService.findByIds(ids);
}
}
|
カスタムPipeの作成#
標準のPipeでは対応できない検証や変換が必要な場合、カスタムPipeを作成します。
すべてのPipeはPipeTransformインターフェースを実装する必要があります。
1
2
3
4
5
6
7
8
9
|
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()
export class CustomPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
// valueを変換または検証してreturn
return value;
}
}
|
ArgumentMetadataには以下の情報が含まれます。
| プロパティ |
説明 |
type |
パラメータの種類(body、query、param、custom) |
metatype |
パラメータの型情報(TypeScriptクラス) |
data |
デコレータに渡された引数(例:@Body('field')のfield) |
変換用カスタムPipeの例#
文字列をトリムして小文字に変換するPipeを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
|
// src/common/pipes/normalize-string.pipe.ts
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()
export class NormalizeStringPipe implements PipeTransform<string, string> {
transform(value: string, metadata: ArgumentMetadata): string {
if (typeof value !== 'string') {
return value;
}
return value.trim().toLowerCase();
}
}
|
使用例:
1
2
3
4
5
6
7
8
|
@Controller('users')
export class UsersController {
@Get('search')
search(@Query('username', NormalizeStringPipe) username: string) {
// usernameは小文字かつトリム済み
return this.usersService.searchByUsername(username);
}
}
|
検証用カスタムPipeの例#
特定のドメインルールに基づいた検証を行うPipeを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// src/common/pipes/parse-positive-int.pipe.ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
BadRequestException,
} from '@nestjs/common';
@Injectable()
export class ParsePositiveIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new BadRequestException(
`${metadata.data || 'パラメータ'}は数値である必要があります`,
);
}
if (parsedValue <= 0) {
throw new BadRequestException(
`${metadata.data || 'パラメータ'}は正の整数である必要があります`,
);
}
return parsedValue;
}
}
|
パラメータ付きカスタムPipeの例#
範囲を指定できるPipeを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
// src/common/pipes/parse-int-range.pipe.ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
BadRequestException,
} from '@nestjs/common';
interface ParseIntRangeOptions {
min?: number;
max?: number;
}
@Injectable()
export class ParseIntRangePipe implements PipeTransform<string, number> {
constructor(private readonly options: ParseIntRangeOptions = {}) {}
transform(value: string, metadata: ArgumentMetadata): number {
const parsedValue = parseInt(value, 10);
if (isNaN(parsedValue)) {
throw new BadRequestException('数値を入力してください');
}
const { min, max } = this.options;
if (min !== undefined && parsedValue < min) {
throw new BadRequestException(`値は${min}以上である必要があります`);
}
if (max !== undefined && parsedValue > max) {
throw new BadRequestException(`値は${max}以下である必要があります`);
}
return parsedValue;
}
}
|
使用例:
1
2
3
4
5
6
7
8
9
10
|
@Controller('articles')
export class ArticlesController {
@Get()
findAll(
@Query('page', new ParseIntRangePipe({ min: 1 })) page: number,
@Query('limit', new ParseIntRangePipe({ min: 1, max: 100 })) limit: number,
) {
return this.articlesService.findAll({ page, limit });
}
}
|
データベースエンティティ取得Pipeの例#
IDからエンティティを取得し、存在しなければ404エラーを返すPipeを作成します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
// src/common/pipes/entity-by-id.pipe.ts
import {
PipeTransform,
Injectable,
ArgumentMetadata,
NotFoundException,
Inject,
} from '@nestjs/common';
import { ArticlesService } from '../../articles/articles.service';
@Injectable()
export class ArticleByIdPipe implements PipeTransform<string, Promise<Article>> {
constructor(
@Inject(ArticlesService) private readonly articlesService: ArticlesService,
) {}
async transform(value: string, metadata: ArgumentMetadata): Promise<Article> {
const id = parseInt(value, 10);
const article = await this.articlesService.findOne(id);
if (!article) {
throw new NotFoundException(`ID ${id} の記事が見つかりません`);
}
return article;
}
}
|
使用例:
1
2
3
4
5
6
7
8
|
@Controller('articles')
export class ArticlesController {
@Get(':id')
findOne(@Param('id', ArticleByIdPipe) article: Article) {
// articleはすでに取得済みのエンティティ
return article;
}
}
|
Pipeの適用スコープ#
Pipeは複数のスコープで適用できます。
パラメータスコープ#
特定のパラメータにのみ適用します。
1
2
3
4
|
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.articlesService.findOne(id);
}
|
メソッドスコープ#
@UsePipes()デコレータを使用して、特定のルートハンドラに適用します。
1
2
3
4
5
6
7
8
9
10
|
import { UsePipes } from '@nestjs/common';
@Controller('articles')
export class ArticlesController {
@Post()
@UsePipes(new ValidationPipe({ transform: true }))
create(@Body() createArticleDto: CreateArticleDto) {
return this.articlesService.create(createArticleDto);
}
}
|
Controllerスコープ#
Controller全体に適用します。
1
2
3
4
5
|
@Controller('articles')
@UsePipes(new ValidationPipe({ whitelist: true }))
export class ArticlesController {
// すべてのハンドラにValidationPipeが適用される
}
|
グローバルスコープ#
main.tsでアプリケーション全体に適用します。
1
2
3
4
5
6
|
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
|
依存性注入を使用してグローバルPipeを登録する場合は、APP_PIPEトークンを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
],
})
export class AppModule {}
|
条件付きバリデーションの実装#
特定の条件下でのみバリデーションを適用したい場合、@ValidateIf()デコレータを使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import { IsString, IsNotEmpty, IsOptional, ValidateIf, IsUrl } from 'class-validator';
export class CreateArticleDto {
@IsString()
@IsNotEmpty()
title: string;
@IsString()
@IsNotEmpty()
type: 'internal' | 'external';
// type が 'internal' の場合のみ content を必須にする
@ValidateIf((obj) => obj.type === 'internal')
@IsString()
@IsNotEmpty()
content?: string;
// type が 'external' の場合のみ externalUrl を必須にする
@ValidateIf((obj) => obj.type === 'external')
@IsUrl()
@IsNotEmpty()
externalUrl?: string;
}
|
バリデーショングループの活用#
同じDTOクラスで複数のバリデーションパターンを定義する場合、グループ機能を使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import { IsString, IsNotEmpty, IsOptional, MinLength } from 'class-validator';
export class UserDto {
@IsString()
@IsNotEmpty({ groups: ['create'] })
@IsOptional({ groups: ['update'] })
username: string;
@IsString()
@MinLength(8, { groups: ['create', 'update'] })
@IsNotEmpty({ groups: ['create'] })
@IsOptional({ groups: ['update'] })
password: string;
@IsString()
@IsOptional({ always: true }) // すべてのグループで常にオプショナル
bio?: string;
}
|
Controllerでグループを指定して検証します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Controller('users')
export class UsersController {
@Post()
@UsePipes(new ValidationPipe({ groups: ['create'] }))
create(@Body() userDto: UserDto) {
return this.usersService.create(userDto);
}
@Patch(':id')
@UsePipes(new ValidationPipe({ groups: ['update'], skipMissingProperties: true }))
update(@Param('id') id: string, @Body() userDto: UserDto) {
return this.usersService.update(id, userDto);
}
}
|
まとめ#
本記事では、NestJSのPipeを活用した入力検証と変換について解説しました。
| トピック |
ポイント |
| Pipeの役割 |
リクエストデータの変換と検証をControllerの前で実行 |
| 組み込みPipe |
ParseIntPipe、ValidationPipe等を用途に応じて使い分け |
| ValidationPipe |
class-validatorと組み合わせてDTOベースの堅牢な検証を実現 |
| カスタムPipe |
ドメイン固有のビジネスロジックに基づいた変換・検証を実装可能 |
| 適用スコープ |
パラメータ、メソッド、Controller、グローバルの4レベルで設定可能 |
Pipeを適切に活用することで、不正なデータがControllerに到達することを防ぎ、セキュアで堅牢なAPIを構築できます。次のステップとして、Guardによる認可処理の実装を学ぶことで、より安全なAPIを構築できるようになります。
参考リンク#