Lesson 4
NestJSをより深く知る
目次
- Chapter1 NestJSをより深く知る
- Chapter2 共通処理の実装
- Chapter3 特定のルーティングで実行する
- Chapter4 ミドルウェアを複数呼び出す
- Chapter5 グローバルに定義する(ミドルウェア)
- Chapter6 オリジナルのデコレーターを作る
- Chapter7 認可処理の実装
- Chapter8 特定の関数に定義する
- Chapter9 グローバルに定義する(ガード)
- Chapter10 pipeを用いたバリデーション実装
- Chapter11 class-validatorのインストール
- Chapter12 共通エラー処理の実装(Interceptor)
- Chapter13 例外処理の実装(catchError)
- Chapter14 特定の関数に定義する(UseInterceptors())
- Chapter15 グローバルに定義する(useGlobalInterceptors)
Lesson 4
Chapter 1
NestJSをより深く知る
Lesson 4ではNestJS
をより深く知るためにNestJS
のより多くの機能を学んでいきます。
ここまでTodoアプリの実装をNestJS
の基本機能を使って作ってきました。これから学んでいくことはそのTodoアプリをよりよいものにするための機能です。この機能を使うことでTodoアプリがよりリッチになったり開発がより行いやすくなります。
実務ではNestJS
の基本機能だけではなく、NestJS
のより多くの機能を使って開発を行います。そのため、ここから学んでいくことは実務に近い知識を学ぶことにも繋がります。
また、NestJS
のバックエンドフレームワークである所以もこのLessonでより明確になっていきます。NestJS
をより深く知っていくことは勉強にもなり、何より楽しいです。
それでは「NestJSをより深く知る」Lessonへ入っていきましょう。

Lesson 4
Chapter 2
共通処理の実装
このChapterではミドルウェアという概念を通して共通処理の実装を学びます。
ミドルウェア
middleware(ミドルウェア)とは「共通処理」を行う関数のことです。「共通処理」は「リクエストとレスポンス」の処理間で発生します。正確にはルートハンドラーが実行される前にミドルウェア関数が実行されます。また、ミドルウェアはnext
という引数を取ります。next
には次のミドルウェアが含まれます。next
を実行して次のミドルウェアに処理を移すことができます。さらに下記図を見ての通りミドルウェアは複数実行できます。
ミドルウェア
ミドルウェアのユースケース
ミドルウェアのユースケースの例としてロガーが挙げられます。ロガーは一定のタイミングで定期的にログを出力する関数です。すべてのリクエストに対してロガーを出力することを考えてみます。その場合、コントローラーすべてのハンドラーに対してconsole.log
もしくはそれをラップした関数を挟み込む必要が出てきます。ミドルウェアであれば一度定義してしまえばあるゆる箇所でそれが適用され何度も同じ処理を書く必要がありません。
ミドルウェアの実装
簡単なロガー用ミドルウェアを実装します。ロガーではリクエストメソッドとリクエストパスを出力しましょう。まずはディレクトリとファイルを作成します。
~/src/(Mac, Linux)
mkdir middlewares && cd middlewares
touch logger.middleware.ts
~/src/(Windows)
mkdir middlewares && cd middlewares
type logger.middleware.ts
ロガーミドルウェアの実装
ミドルウェアはクラスと関数どちらでも実装できます。公式ではシンプルな関数ミドルウェアを推奨しているので関数ミドルウェアを実装します。まずは簡単な文字列を出力するだけのロガーミドルウェアを実装します。最後に必ずnext
を実行しましょう。
~/src/middlewares/logger.middleware.ts
import { Request, Response, NextFunction } from 'express';
export const logger = (req: Request, res: Response, next: NextFunction) => {
console.log(`Request...`);
next();
};
ミドルウェアの登録
ミドルウェアを実装したのでアプリケーションに登録します。ミドルウェアの登録は「app.module.ts」にて行います。AppModule
のconfigure
メソッド内でミドルウェアを登録します。forRoutes
でルートを指定できますが今回はすべてのリクエストで行うため/
としています。
~/src/app.module.ts
@Module({
...省略
})
// 追加
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(logger).forRoutes('/');
}
}
ミドルウェアの実行
リクエストを送信してミドルウェアを実行します。ログに指定した文字列が表示されれば成功です。「http://localhost:3090/todo/」へ対してリクエストを送信します。下記のようにログに表示されたでしょうか?
ターミナル
Request...
リクエストログ用のミドルウェア
最後にリクエスト情報をログとして出力するミドルウェアを作成します。情報としてはリクエストメソッドとリクエストURLを表示するようにしてみましょう。先ほどのミドルウェアをアップデートします。
~/src/middlewares/logger.middleware.ts
...省略
export const logger = (req: Request, res: Response, next: NextFunction) => {
// 変更
console.log(`${req.method.toUpperCase()}: ${req.url}`);
next();
};
「http://localhost:3090/todo/」へリクエストを送信すると下記のように表示されます。
ターミナル
POST: /todo
まとめ
今回はログ用のミドルウェアを実装しましたが、ライブラリとして公開されているミドルウェアも多々存在します。たとえば代表的なWebセキュリティ攻撃のXSS(クロスサイトスクリプティング)を防止するhelmet
等のライブラリはミドルウェアを公開しており、リクエストごとに実行する必要があります。このようにミドルウェアは複数呼ぶことが可能です。通常のプロジェクトでは複数のミドルウェアを実行しながらさまざまな共通処理を実行していくことになります。

Lesson 4
Chapter 3
特定のルーティングで実行する
「Chapter4.2 共通処理の実装」ではすべてのルーティングに対してミドルウェアが実行される仕組みにしました。ミドルウェアは特定のルーティングのみで実行することもできます。
特定のルーティングで実行する:forRoutes
「Chapter4.2 共通処理の実装」ではconsumer.apply(logger).forRoutes('/');
によってミドルウェアの登録を行いました。forRoutes
の引数によってミドルウェアの呼び出しを特定のルートに制限できます。たとえば「/todo」のみで実行したいミドルウェアが存在するときは下記のようにcontrollerを登録します。
consumer.apply(logger).forRoutes(TodoController);
特定のルートを除外する:exclude
exclude
を使用することで特定のルートを除外できます。「/todo」をforRoutes
に登録した状態でTodo削除のハンドラーをロガーから除外してみます。「http://localhost:3090/todo/TODOのID」へDELETEリクエストを送っても何もログが表示されないことを確認できます。exclude
にオブジェクトで指定する場合はpath
とmethod
が必須パラメーターとなります。
app.modules.ts
...省略
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(logger)
// DELETEメソッドをミドルウェアから除外
.exclude({
path: 'todo/:id',
method: RequestMethod.DELETE,
})
.forRoutes(TodoController);
}
}
まとめ
ミドルウェアは特定のルートに制限して実行できることと特定のルートを除外して実行できることが確認できました。どちらもミドルウェアを使用する上で必須な概念となりますのでしっかり復習しておきましょう。

Lesson 4
Chapter 4
ミドルウェアを複数呼び出す
ミドルウェアを同時に複数呼び出すことが可能です。まずは必要なライブラリのインストールを行います。
helmet
helmetはWebの脆弱性からアプリケーションを守ってくれるライブラリです。具体的にはレスポンスヘッダー内で必要な情報を付与、不要な情報を削除します。下記でインストールを行いましょう。
~/src
npm i helmet
cors
corsとはオリジン間リソース共有 (Cross Origin Resource Sharing)のことを言います。
この機能によってオリジン間のリソース共有を制限・許可できます。
通常、http://localhost:3000/search
からhttp://localhost:3090/search
へリクエストを送る場合、
ブラウザはcors
によってこのリクエストをブロックします。この場合、http://localhost:ポート番号/
までがオリジンにあたります。このリクエストを許可するために以下の処理が必要になります。
~/src/middlewares/cors.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class CorsMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept',
);
next();
}
}
現在のレスポンスヘッダー情報
ミドルウェアを複数設定する前に現在のレスポンスヘッダーがどのようになっているか確認してみます。「http://localhost:3090/todo/」へGETリクエスを送信し、ThunderClientのHeadersを確認してみます。「x-powered-by」というヘッダーが含まれています。このヘッダー情報は脆弱性に関わる情報を含みMDNでも含めないよう警告されていますので消す必要があります。
レスポンスヘッダー情報
helmetミドルウェアの適用
冒頭でインストールしたhelmetをミドルウェアとして登録します。
~/src/app.module.ts
...省略
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
// 追加
.apply(helmet())
// 追加
.forRoutes('/')
.apply(logger)
.exclude({
path: '/todo/:id',
method: RequestMethod.DELETE,
})
.forRoutes(TodoController);
}
}
レスポンスヘッダー情報
helmet適用後レスポンスヘッダーを確認してみます。同様に「http://localhost:3090/todo/」へGETリクエストを送信します。レスポンスヘッダー情報は下記になります。たくさん追加されていますが、「x-powered-by」が含まれてないことが確認できます。
helmet適用後レスポンスヘッダー
corsミドルウェアの適用
作成したcorsミドルウェアを登録します。corsのミドルウェア適用後は「access-control-allow-origin」と「access-control-allow-headers」が追加されることを確認します。
~/src/app.module.ts
...省略
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
// 追加
.apply(helmet(), CorsMiddleware)
.forRoutes('/')
.apply(logger)
.exclude({
path: '/todo/:id',
method: RequestMethod.DELETE,
})
.forRoutes(TodoController);
}
}
レスポンスヘッダー情報
cors適用後レスポンスヘッダーを確認してみます。同様に「http://localhost:3090/todo/」へGETリクエストを送信します。レスポンスヘッダー情報に「access-control-allow-origin」と「access-control-allow-headers」が追加されたことが確認できます。現在は「access-control-allow-origin」が*
となっておりどこからでもリクエストができる状態になっています。実際の開発ではこの値を制限してリクエスト元を制限します。
レスポンスヘッダー情報
まとめ
セキュリティ関連のミドルウェアを適用することが複数のミドルウェアを実行できることが確認できました。helmetとcorsはNodejsを用いて開発する際ほとんどのプロジェクトで使用されます。つまり複数のミドルウェア実行は必須の知識となりますのでしっかり復習を行いましょう。

Lesson 4
Chapter 5
グローバルに定義する(ミドルウェア)
「Chapter 4 ミドルウェアを複数呼び出す」ではforRoutes('/')
を指定してミドルウェアを呼び出す方法を学びました。このChapterでは「Chapter 4.4
ミドルウェアを複数呼び出す」で設定したミドルウェアをforRoutes
なしでグローバルに登録する方法を学びます。
ミドルウェアをグローバルに定義する
ミドルウェアをグローバルに定義するということは、「登録されているすべてのハンドラーに対してミドルウェアを実行する」ということです。グローバル定義を行うにはNestJSのインスタンスが提供するuse
メソッドを使用します。
~/src/main.ts
import { NestFactory } from '@nestjs/core';
// 追加
import helmet from 'helmet';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 追加
app.use(helmet());
await app.listen(3090);
}
bootstrap();
まとめ
登録後「http://localhost:3090/todo/」へGETリクエストを行うとforRoutes
でhelmetミドルウェアを登録した時と変わらないレスポンスヘッダー情報が確認できました。これでミドルウェアのグローバル定義が完了です。

Lesson 4
Chapter 6
オリジナルのデコレーターを作る
NestJS
には多くのデコレーターが存在しますが、ユーザー定義のオリジナルデコレーターも作成できます。オリジナルデコレーターを作成する目的はアプリケーションのロジックをより宣言的に表現することです。この章ではオリジナルデコレーターを作成し、それを使用してアプリケーションのロジックをより宣言的に表現する方法を学びます。
ロジックを宣言的に記述する
デコレーターを使用してロジックを宣言的に記述するとはどういうことか見ていきます。
Request
オブジェクトからユーザーIDを抽出する
たとえば多くのリクエストでユーザーIDを抽出しそれを使用したいケースがあるとします。ユーザーIDを抽出するためにはreq
オブジェクトからuser
オブジェクトを取得し、その中からid
を取得するという手続きを踏む必要があります。以下のコードはその例を示しています。書く必要はありません。
ユーザーIDを取得する例
@Get(':id')
async myController(
@Request req: Request,
): Promise<MyDto> {
const userId = req.user.id;
return await this.myService.myMethod(userId);
}
デコレーターで宣言的にユーザーIDを抽出する
上記コードは以下のように書き換えることができます。オブジェクトからuserId
を取得するという手続きを踏むことなくuserId
を取得できています。あたかもuserId
がそのまま引数に入ってくるように宣言的な記述で実現できました。
ユーザーIDを取得する例(デコレーター書き換え後)
@Get(':id')
async myController(
@GetTodoId() userId: string, // 変更
): Promise<MyDto> {
return await this.myService.myMethod(userId);
}
オリジナルデコレーター(カスタムデコレーター)
デコレーターを作成するにはcreateParamDecorator
関数を使用します。この関数を使用してデコレーターを作成します。
ディレクトリとファイルを作成する
デコレーターを作成するためsrc/
直下にdecorators
ディレクトリを作成します。
~/src/(Mac, Linux)
mkdir decorators && cd decorators
touch todo.decorator.ts
~/src/(Windows)
mkdir decorators && cd decorators
type todo.decorator.ts
デコレーターを作成する
以下のコードはGetTodoId
デコレーターを作成するコードです。src/decorators/todo.decorator.ts
に記述します。このデコレーターは@GetTodoId()
という形で使用します。
~/src/todo/todo.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const GetTodoId = createParamDecorator(
(data: unknown, ctx: ExecutionContext): string => {
const request = ctx.switchToHttp().getRequest();
return request.params.id as string;
},
);
デコレーターを使用する
以下のコードはGetTodoId
デコレーターを使用するコードです。デコレーターを使用すると手続きを踏むことなく宣言的にtodoId
を取得できます。
~/src/todo/todo.controller.ts
...省略
@Delete(':id')
async deleteTodo(
@Param() param: DeleteTodoRequestDTO,
@GetTodoId() id: string,
): Promise<DeleteTodoResponseDTO> {
console.log('id: ', id);
return await this.todoService.deleteTodo(param.id);
}
...省略
まとめ
デコレーターを使用することで手続きを踏むことなく宣言的にtodoId
を取得できました。デコレーターを使用することでコードの可読性が向上し、コードの記述量が減ります。今後のChapterで使用するguard
やinterceptor
もデコレーターを使用して実装します。

Lesson 4
Chapter 7
認可処理の実装
このChapterでは認可処理の実装を行います。認可処理とは、ユーザーがリソースに対してアクセスできるかどうかを判定する処理です。認可処理はNestJS
のguard
という機能を使用して実装します。このChapterではguard
を使用して認可処理を実装するところまでを行います。
実装の方針
認可にはほとんど認証が伴います。しかし、認証処理の実装は難しく解説も多くなってしまいます。今回は認可処理(guard
)の解説という本質に集中するため、認証処理は実装せずに認可のための仮実装を行います。
認可のための仮実装
今回の認可処理のフローとしては下記になります。
- クライアントがリクエストを送信する
- NestJSでリクエストを受け取り、
guard
を使用して認可処理を行う - 認可処理が通ればそのまま通常のリクエスト処理を実施する
- 認可処理が通らなければ403エラーが返る
認可の条件
今回の認可の条件としてリクエストヘッダーにロールを所持していることと定義します。また認可処理が通る条件としては、ハンドラーが持っているロールとユーザーが所持しているロールが合致していることとします。 イメージが湧かないと思うので早速実装していきましょう。
認可処理の実装
認可処理を実装するにはguard
を使用します。
ディレクトリとファイルを作成する
認可処理を実装するためsrc/
直下にguards
ディレクトリを作成します。
~/src/(Mac, Linux)
mkdir guards && cd guards
touch roles.guard.ts
~/src/(Windows)
mkdir guards && cd guards
type roles.guard.ts
ロールを定義する
アプリケーションの中で使用するロールを事前に定義しておきましょう。この工程を行うことでハードコーディングを避けることができ、人的ミスの防止につながります。
ハードコーディング
ハードコーディングとは、ソースコードに直接値を記述することを指します。ハードコーディングを行うと、ソースコードの可読性が下がり、コードの修正が難しくなります。
~/src/constants/index.ts
export const TODO_IMAGE_FILE_PATH = './files';
export const BASE_URL = 'http://localhost:3090';
// 以下を追加
export const META = {
roles: {
admin: 'admin',
},
};
ハンドラーにロールを付与する
認可処理を実装するためにはハンドラーにロールを付与する必要があります。今回はTodoController
のfindAll
メソッドにロールを付与します。ハンドラーにロールを付与するためには@SetMetadata
デコレーターを使用します。@SetMetadata
デコレーターを使用することでハンドラーにメタデータを付与できます。
~/src/todo/todo.controller.ts
...省略
import { BASE_URL, META, TODO_IMAGE_FILE_PATH } from 'src/constants';
...省略
@Controller('todo')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@Get()
@SetMetadata('roles', [META.roles.admin]) // 追加
async findAll(): Promise<FindAllTodoResponseDTO> {
return await this.todoService.findAll();
}
...省略
}
ロール付与用の関数をモジュール化する
毎回@SetMetadata('roles', ~)
と書くのはミスも生じるためこちらをモジュール化しましょう。デコレーターとして使用したいのでカスタムデコレーターを定義します。
~/src/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
import { META } from 'src/constants';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
ロール付与用の関数を使用する
先ほど作成したRoles
関数を使用してハンドラーにロールを付与します。
~/src/todo/todo.controller.ts
...省略
import { Roles } from 'src/decorators/roles.decorator'; // 追加
...省略
@Controller('todo')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@Get()
@Roles(META.roles.admin) // 追加
async findAll(): Promise<FindAllTodoResponseDTO> {
return await this.todoService.findAll();
}
...省略
}
ガードとは
ガードの役割は認可を行うことたった1つです。認可は、ユーザーがリクエストを送信した際に、そのリクエストを許可するか拒否するかを決定する処理です。リソースとはこの場合付与するハンドラーに該当します。
ガードを実装する
src/guards/roles.guard.ts
にガードを実装します。ガードは@Injectable()
デコレータで装飾されたクラスであり、canActivate
メソッドの実装(implements
)が必須です。reflector
を使用してメタデータにアクセスします。
また、ガードは付与されているハンドラーのコンテキストを取得できます。コンテキストを取得することで、ハンドラーに付与されているロールを取得できます。
~/src/guards/roles.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
// 付与されているハンドラーのロールを取得
const handlerRoles = this.reflector.get<string[]>(
'roles',
context.getHandler(),
);
// ロールが付与されていない場合は認可を行わずすべてのリクエストを許可
if (!handlerRoles) {
return true;
}
// リクエストのロールを取得
const request = context.switchToHttp().getRequest();
const userRole = request.get('role');
// リクエストのロールがハンドラーのロールに含まれているかを返す
return handlerRoles.some((role) => userRole === role);
}
}
まとめ
今回は認可を行うガードを実装しました。次のChapterで実装したガードを元にハンドラーへ付与し認可処理を行っていきます。

Lesson 4
Chapter 8
特定の関数に定義する
このChapterでは特定の関数(ハンドラー)にガードを付与する方法を学びます。ガードは前回のChapterで実装したRolesGuard
を使用します。Todoの一覧を取得するハンドラーにガードを付与します。
特定の関数に定義する
ガードを特定の関数に定義するには、@UseGuards()
デコレータを使用します。
~/src/todo/todo.controller.ts
...省略
import { RolesGuard } from 'src/guards/roles.guard'; // 追加
...省略
@Controller('todo')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
@Get()
@Roles(META.roles.admin)
@UseGuards(RolesGuard) // 追加
async findAll(): Promise<FindAllTodoResponseDTO> {
return await this.todoService.findAll();
}
...省略
}
ガードの実装を確かめる
実装したガードがちゃんと動くのか確かめてみましょう。ガード実装の詳細を振り返ってみるとconst userRole = request.get('role');
という記述があります。こちらはリクエストヘッダーからrole
というキーを用いて値を取得する処理です。よってリクエストヘッダーにrole
というキーで値をセットする必要があります。
認可が動作するか確かめる
最初は認可処理が正常に動作しているか確かめるためにわざと認可に通らない状況でリクエストを送信してみます。ThunderClientのヘッダーを初期状態でリクエストを送信してみましょう。
わざと認可を通らないリクエスト送信
すると以下のjson
が返されます。こちらはNestJS
の機能であるExceptionFilter
が働いた結果返されます。今回はその中の1つのUnauthorizedException
が返された結果です。
ExceptionFilter
ExceptionFilter
にはたくさん種類があるので興味がある方はこちらをのぞいてみてください。
Todo一覧レスポンス
{
"statusCode": 403,
"message": "Forbidden resource",
"error": "Forbidden"
}
認可を通すリクエストを送信する
次に認可を通すリクエストを送信してみます。ThunderClientのヘッダーにrole
というキーで「admin」という値をセットしましょう。リクエストを送信すると通常通りTodoの一覧が返却されることを確認できます。これで認可が正常に動作していることを確認できました。
認可を通すリクエスト送信
まとめ
このChapterでは特定の関数にガードを付与する方法を学びました。次はグローバルにガードを適用させる方法を学びます。

Lesson 4
Chapter 9
グローバルに定義する(ガード)
このChapterではガードをグローバルに適用する方法を学びます。前回は特定のハンドラーのみに付与したため、Todo一覧のハンドラーにのみガードが適用されていました。グローバルにガードを適用することで他のハンドラーでもガードが動作するか確かめていきましょう。
グローバルにガードを定義する
まずはapp.module.ts
にガードを定義します。今回はRolesGuard
をグローバルに適用させます。そのためにはapp.module.ts
の@Module
デコレーターのproviders
にRolesGuard
を追加します。
app.module.ts
...省略
import { RolesGuard } from './roles.guard';
...省略
@Module({
imports: [
...省略
// 追加
providers: [
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
],
})
リクエストを送信する
グローバルに定義したガードが動作しているか確かめてみます。「Chapter4.4.2
特定の関数に定義する」と同じようにTodo一覧取得リクエストを送信してみましょう。その際に以下の画像のようにrole
のチェックは外しておきます。ステータスコード403
でUnauthorizedException
の内容が返却されれば認可処理が通っていることを確認できます。
認可を通さないリクエスト送信
Todo作成用のハンドラーにロールを付与する
グローバルに適用しているため他のハンドラーにも認可処理が働くか確かめてみましょう。まずはTodo作成用のハンドラーにメタデータを付与します。
~/src/controllers/todo/todo.controller.ts
...省略
@Controller('todo')
export class TodoController {
constructor(private readonly todoService: TodoService) {}
...省略
@Post()
@Roles(META.roles.admin) // 追加
@UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: TODO_IMAGE_FILE_PATH,
filename: generateFilename,
}),
}),
)
async createTodo(
@Body() createTodoDTO: CreateTodoRequestDTO,
@UploadedFile() file: Express.Multer.File,
): Promise<CreateTodoResponseDTO> {
const imagePath = file?.path ? `${BASE_URL}/${file.path}` : null;
return await this.todoService.createTodo(createTodoDTO, imagePath);
}
...省略
}
Todo作成用のハンドラーにリクエストを送信する
Todo作成用のハンドラーにメタデータを付与したので、リクエストを送信してみましょう。その際に以下の画像のようにrole
は付与せず送信します。ステータスコード403
でUnauthorizedException
の内容が返却されれば認可処理が通っています。
認可を通さないリクエスト送信
まとめ
このChapterではガードをグローバルに適用する方法を学びました。前回は特定のハンドラーのみに付与したため、Todo一覧のハンドラーにのみガードが適用されていました。グローバルにガードを適用することで他のハンドラーでもガードが動作するか確かめることができました。

Lesson 4
Chapter 10
pipeを用いたバリデーション実装
このChapterではバリデーションを実装する方法を学びます。バリデーションを実装することで、emailやパスワードの形式チェック等でリクエストの内容が正しいかどうかを確認し、不正なリクエストを受け付けないようにすることができます。NestJSではPipe
という機能を用いてバリデーションを行います。
Pipe
Pipe
はハンドラーがリクエストを受け取る前にリクエストに対して処理を行います。Pipe
では以下のことが可能で、処理を行った後のデータをハンドラーに渡します。
説明 | |
---|---|
変換 | 入力データを希望の形に変換する(例:文字列→整数) |
バリデーション | 入力データを評価し、データが正しければそのまま実行を続け、データが間違っていれば例外をthrowする |
組み込みのPipe
NestJSには組み込みのPipe
が用意されています。以下のPipe
は@nestjs/common
からインポートできます。
名前 | 説明 |
---|---|
ValidationPipe | バリデーションを行う |
ParseIntPipe | 文字列を整数に変換する |
ParseBoolPipe | 文字列を真偽値に変換する |
ParseArrayPipe | 文字列を配列に変換する |
ParseUUIDPipe | 文字列をUUID 型であるかバリデーションを行う |
DefaultValuePipe | 入力がnull またはundefined 時にデフォルト値を付与する |
Pipeの適用方法
Pipe
はハンドラーの引数に付与することで適用できます。以下の例ではValidationPipe
をハンドラーの引数に付与しています。
ValidationPipe
をハンドラーの引数に付与することで、createTodoDto
に対してバリデーションが行われます。
ハンドラへの適用(例)
@Post()
@UsePipes(ValidationPipe)
create(@Body() createTodoDto: CreateTodoDto) {
return this.todoService.create(createTodoDto);
}
また、パラメーターへの適用も可能です。パラメーターごとに型が異なるためより柔軟にすることができます。
パラメーターごとへの適用(例)
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.todoService.findOne(id);
}
また、グローバルへの適用も可能です。グローバルに適用することで、すべてのハンドラーに対してValidationPipe
が適用されます。
グローバルへの適用(例)
app.useGlobalPipes(new ValidationPipe());
pipeを用いたバリデーション実装
ここではParseUUIDPipe
を用いてバリデーションを実装します。Todo詳細取得ハンドラーのfindOne
メソッドを以下のように修正します。
~/src/controllers/todo/todo.controller.ts
import {
Body,
Controller,
Delete,
Get,
Param,
ParseUUIDPipe, //追加
Post,
Put,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
...省略
@Get(':id')
async findOne(
@Param('id', ParseUUIDPipe) { id }: FindOneTodoRequestDTO,
): Promise<FindOneTodoResponseDTO> {
return await this.todoService.findOne(id);
}
...省略
リクエストを送信する
ParseUUIDPipe
のバリデーションが働くか確認するためにリクエストを送信して確かめてみます。以下のようにThunderClientからリクエストを送信します。リクエストパラメーターのidの部分をわざとUUID
以外の文字列にして送信してみます。
ThunderClient Todo詳細取得
バリデーションエラーを確認する
上記のようにリクエストを送信すると、以下のようにバリデーションエラーが返却されます。よって、ParseUUIDPipe
のバリデーションが働いていることが確認できます。
ThunderClient レスポンス
{
"statusCode": 400,
"message": "Validation failed (uuid is expected)",
"error": "Bad Request"
}
まとめ
ここではPipe
を用いてバリデーションを実装する方法を学びました。次のChapterからはclass-validator
を用いてバリデーションを実装する方法を学びます。class-validator
を使用するとDTOに対してバリデーションを実装することができます。

Lesson 4
Chapter 11
class-validatorのインストール
class-validator
class-validator
は入力バリデーション用のライブラリです。DTOクラスに対して付与することでバリデーションを実装することができます。class-validatorのGitHubで用意されてるバリデーションを確認できます。
class-validatorのインストール
まずはclass-validator
をインストールします。class-validator
の使用にはclass-transformer
も必要なので一緒にインストールします。インストールするためにプロジェクト直下へ移動しましょう。
~/nest-todo
npm i class-validator class-transformer
まとめ
ここではclass-validator
のインストール方法を学びました。次のChapterからはclass-validator
を用いてバリデーションを実装する方法を学びます。

Lesson 4
Chapter 4.5.3
class-validatorを用いたバリデーション実装
ここではclass-validator
を用いてバリデーションを実装します。
class-validatorの実装
まずはclass-validator
を用いてFindOneTodoRequestDTO
にバリデーションを実装します。@IsUUID()
を付与することでid
にUUIDのバリデーションを実装できます。
~/nest-todo/src/todo/dto/find-one-todo-request.dto.ts
import { IsUUID } from 'class-validator';
export class FindOneTodoRequestDTO {
@IsUUID()
id: string;
}
Pipeのグローバル適用
ここまででFindOneTodoRequestDTO
にバリデーションを実装しました。しかし、現状ではFindOneTodoRequestDTO
にバリデーションを実装しても、FindOneTodoRequestDTO
を使用しているfindOneTodo
にバリデーションが適用されていません。そのため、findOneTodo
にバリデーションを適用するためには、FindOneTodoRequestDTO
を使用しているfindOneTodo
に@UsePipes
を付与する必要があります。しかし、毎回ハンドラーに対して@UsePipes
を付与するのは面倒なので、Pipe
をグローバルに適用します。
~/nest-todo/src/main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import helmet from 'helmet';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(helmet());
app.useGlobalPipes(new ValidationPipe()); // 追加
await app.listen(3090);
}
bootstrap();
リクエストを送信する
ここまででFindOneTodoRequestDTO
にバリデーションを実装し、Pipe
をグローバルに適用しました。次にThunderClientでリクエストを送信してバリデーションが適用されているか確認してみましょう。
ThunderClientでリクエストを送信
レスポンスを確認する
「リクエストを送信する」の結果以下のようにレスポンスが返却されているとclass-validator
を用いたバリデーションが成功しています。
ThunderClientレスポンス
{
"statusCode": 400,
"message": [
"id must be a UUID"
],
"error": "Bad Request"
}
各DTOにバリデーションを付与していく
ここまででFindOneTodoRequestDTO
にバリデーションを実装しました。他のDTOにもバリデーションを実装していきます。
CreateTodoRequestDTO
CreateTodoRequestDTO
では以下のバリデーションを利用します。
バリデーション | 説明 |
---|---|
@IsString() |
文字列であることを確認する |
@IsNotEmpty() |
空文字でないことを確認する |
@MaxLength(256) |
最大文字数を256文字に設定する |
@IsBoolean() |
真偽値であることを確認する |
@IsOptional() |
空文字であっても良いことを確認する |
~/nest-todo/src/requests/todo/create.dto.ts
import {
IsBoolean,
IsNotEmpty,
IsOptional,
IsString,
MaxLength,
} from 'class-validator';
export class CreateTodoRequestDTO {
@IsString()
@IsNotEmpty()
@MaxLength(256)
title: string;
@IsBoolean()
@IsOptional()
isCompleted?: boolean;
}
たとえばバリデーションに反したリクエストを送信すると下記のようにバリデーションメッセージが返却されます。
Thunder Clientレスポンス
{
"statusCode": 400,
"message": [
"title must be shorter than or equal to 256 characters",
"title should not be empty",
"title must be a string"
],
"error": "Bad Request"
}
UpdateTodoRequestDTO
UpdateTodoRequestDTO
でもCreateTodoRequestDTO
と同じバリデーションを利用します。
~/src/requests/todo/update.dto.ts
import {
IsBoolean,
IsNotEmpty,
IsOptional,
IsString,
MaxLength,
} from 'class-validator';
export class UpdateTodoRequestDTO {
@IsString()
@IsOptional()
@IsNotEmpty()
@MaxLength(256)
title?: string;
@IsBoolean()
@IsOptional()
isCompleted?: boolean;
}
DeleteTodoRequestDTO
DeleteTodoRequestDTO
では@IsUUID()
を利用します。
~/src/requests/todo/delete.dto.ts
import { IsUUID } from 'class-validator';
export class DeleteTodoRequestDTO {
@IsUUID()
@IsNotEmpty()
id: string;
}
まとめ
ここまででclass-validator
を用いたバリデーションを実装しました。DTO
に対してバリデーションを実装することで、DTO
ではリクエストオブジェクトの定義と同時にバリデーションを隠蔽することができ、controllerの記述をよりシンプルに保つことができるようになりました。

Lesson 4
Chapter 12
共通エラー処理の実装(Interceptor)
このChapterではエラー処理を行いながらInterceptor
について学習します。
Interceptor
とは、リクエストの前後に処理を挟むことができるNestJSの機能です。
Interceptor
を利用することで、ハンドラーの直後にロジックを追加できたり、関数のレスポンスを変換したり、例外処理を行ったりさまざまなことができます。
Interceptor
の基本
Interceptor
はNestInterceptor
を実装することで作成できます。以下はロギングを行うInterceptor
の例です。Interceptor
は必ずintercept
メソッドを持ちます。intercept
はリクエストとレスポンスの結果を受け取る事ができるため付与するハンドラー本体の実行前後に処理を挿入する事ができます。第1引数のcontext
はExecutionContext
で、リクエストの情報を保持しています。第2引数のnext
はCallHandler
で、ハンドラーの実行を行うためのメソッドを持っています。下記実装の場合Before...
がリクエストの前に、After... \${Date.now() - now}ms
がレスポンスの後に出力されます。
~/src/interceptors/logging.interceptor.ts
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
console.log('Before...');
const now = Date.now();
return next.handle().pipe(
tap(() => console.log(\`After... \${Date.now() - now}ms\`)),
);
}
}

Lesson 4
Chapter 13
例外処理の実装(catchError)
このChapterではInterceptor
を使用して例外処理の実装を学びます。
Interceptor
を使った例外処理
Interceptor
を使用することで、共通の例外処理をハンドラーに適用できます。今回は途中で例外がthrow
された場合そのままthrow
し、サーバーエラーが発生した場合は例外フィルターを返すような実装を行います。catchError
はthrow
されたエラーオブジェクトを捕捉してInterceptor
内で操作できるようにしてくれます。
~/src/interceptors/errors.interceptor.ts
import {
CallHandler,
ExecutionContext,
HttpException,
Injectable,
InternalServerErrorException,
NestInterceptor,
} from '@nestjs/common';
import { Observable, catchError, throwError } from 'rxjs';
@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable {
return next.handle().pipe(
catchError((err) => {
console.log('err: ', err); // ログを監視してInterceptorによって例外が投げられているか確認
if (err instanceof HttpException) {
return throwError(() => err);
}
return throwError(() => new InternalServerErrorException());
}),
);
}
}
エラーを発生させる処理を追加
重複チェック repository
今回はTodo作成時に同じ名前のTodoが既に存在すれば例外をthrow
するようにしましょう。Todo用のrepository
に処理を追加します。repository
の処理にはtiteをキーにTodoを取得し、Todoオブジェクトが取得できればboolean
型で結果を返すようにします。
~/src/repositories/todo/todo.repository.ts
...省略
async getTodoDetail(id: string): Promise<ReturnTodoType> {
return await this.findOne({
select: SELECTED_COLUMN,
where: { id },
});
}
// 下記処理を追加
async checkTodoExists(title: string): Promise<boolean> {
const todo = await this.findOne({
select: SELECTED_COLUMN,
where: { title },
});
return !!todo;
}
async createTodo(
createTodoRequestDTO: CreateTodoRequestDTO,
imagePath: string,
): Promise<ReturnTodoType> {
...省略
重複チェック service
service
ではrepositoryからの結果をもとにエラーをthrow
します。
~/src/services/todo/todo.service.ts
...省略
async createTodo(
createTodoDTO: CreateTodoRequestDTO,
imagePath: string,
): Promise {
const isExists = await this.todoRepository.checkTodoExists(
createTodoDTO.title,
);
if (isExists) {
throw new Error('Todo title already exists');
}
const newTodo = await this.todoRepository.createTodo(
createTodoDTO,
imagePath,
);
return new CreateTodoResponseDTO(newTodo);
}
まとめ
今回はInterceptor
を使用して例外処理を実装しました。次のChapterでは定義したInterceptor
をcontroller
に適用させていきます。

Lesson 4
Chapter 14
特定の関数に定義する(UseInterceptors())
このChapterでは定義したInterceptor
を特定の関数に適用する方法を学びます。
UseInterceptors()
UseInterceptors()
は特定の関数(controller全体やcontroller内のハンドラー)に対してInterceptor
を適用するためのデコレーターです。引数にはInterceptor
を定義したクラスを渡します。
controllerへの適用
今回はTodoController
にErrorsInterceptor
を適用します。
~/src/controllers/todo/todo.controller.ts
...省略
import { ErrorsInterceptor } from 'src/interceptors/errors.interceptor'; // 追加
...省略
@UseInterceptors(ErrorsInterceptor) // 追加
@Controller('todo')
export class TodoController {
...省略
Thunder Clientでリクエストの送信
適用できたらThunder
ClientからTodo作成のリクエストを送信しましょう。ここではわざと同じ名前のTodoをリクエストパラメーターとして送信し例外を発生させます。その結果下記のようにログに出力されればInterceptor
適用の成功です。
エラーオブジェクトの内容を確認
まとめ
今回はUseInterceptors()
を使用して特定の関数にInterceptor
を適用する方法を学びました。次のChapterではInterceptor
をグローバルに適用させていきます。

Lesson 4
Chapter 15
グローバルに定義する(useGlobalInterceptors)
このChapterでは定義したInterceptor
をグローバルに適用する方法を学びます。グローバルインターセプターは全てのコントローラと全てのルートハンドラに対してアプリケーション全体を通して使用されます。
useGlobalInterceptors()
useGlobalInterceptors()
はInterceptor
をグローバルに適用するためのメソッドです。引数にはInterceptor
を定義したクラスを渡します。
main.tsへの適用
今回はmain.ts
にErrorsInterceptor
を適用します。適用したら同じようにリクエストを送信してエラーのログが出力されれば適用成功です。
~/src/main.ts
...省略
import { ErrorsInterceptor } from './interceptors/errors.interceptor'; // 追加
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(helmet());
app.useGlobalPipes(new ValidationPipe());
app.useGlobalInterceptors(new ErrorsInterceptor()); // 追加
await app.listen(3090);
}
bootstrap();
...省略
まとめ
今回はuseGlobalInterceptors()
を使用してグローバルにInterceptor
を適用する方法を学びました。Interceptor
を使用することでさまざまな処理が柔軟に実装できるようになりますので積極的に使っていきましょう。

目次
- Chapter1 NestJSをより深く知る
- Chapter2 共通処理の実装
- Chapter3 特定のルーティングで実行する
- Chapter4 ミドルウェアを複数呼び出す
- Chapter5 グローバルに定義する(ミドルウェア)
- Chapter6 オリジナルのデコレーターを作る
- Chapter7 認可処理の実装
- Chapter8 特定の関数に定義する
- Chapter9 グローバルに定義する(ガード)
- Chapter10 pipeを用いたバリデーション実装
- Chapter11 class-validatorのインストール
- Chapter12 共通エラー処理の実装(Interceptor)
- Chapter13 例外処理の実装(catchError)
- Chapter14 特定の関数に定義する(UseInterceptors())
- Chapter15 グローバルに定義する(useGlobalInterceptors)