NestJS 例外処理・例外Fileter まとめ
こんにちは。豆太郎です。
アルバイトで、NestJSの開発に取り組んでいます。
NestJSの公式ドキュメントを読んで、例外処理やFilterの仕組みについて学んだのでそのまとめ記事を書きたいと思います。
NestJSの例外処理について
- 例外処理は、HttpExceptionを継承することでApplicationExceptionやBadRequestExceptionなど、HTTP statusに応じたの複数の例外処理を作成することができる。
export class BadRequestException extends HttpException {
constructor() {
super('BadRequest', HttpStatus.BadRequest);
}
}
例外処理の使われ方
@Get()
async findAll() {
try {
await this.service.findAll()
} catch (error) {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, HttpStatus.FORBIDDEN, {
cause: error
});
}
}
また上記のように、findAll関数の中でtryを使用して、処理に成功した場合に、findAllを実行して、失敗した場合にthorw new ”例外処理”を呼び出すケースが多い。
さらに、例外処理をthrow newする際に、messageや、causeなどの引数をつけることで、レスポンスにその引数の結果を返すことができる。
throw new BadRequestException('Something bad happened', { cause: new Error(), description: 'Some error description' })
上記の結果
{
"message": "Something bad happened",
"error": "Some error description",
"statusCode": 400,
}
このように引数としてメッセージを付け加えることのメリットとしては、その例外処理が何かを原因を詳しく知ることに役立つことがあげられる。例えば、登録画面のフォームにおいての入力が誤っており、Nest側から(バックエンド)でBad Requestが発生した場合に、そのBad Requestのmessageの引数に”登録フォームの入力が不正です”といったメッセージを記載しておけば、エラーが生じた際に、例外処理の原因を突き止めることができる。
Exceptionfilterとは?
Exceptionfilterとは、いくかの例外処理に関して、クライアント側に返す処理をまとめて管理することのできるインスタンスである。例えば、ExceptionFilterは、クライアント側に返すレスポンスに関して、メッセージ・例外が発生した時間・エラーコードなどを自由に変更することができる。
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
上記はExceptionFilterである。上記のコードでは、HttpExcptionExceptionFilterが実装されている。ここでは、@Catch(HttpException)のように、HttpExceptionが引数として受け取ることができる。ただし、@Catch()のように引数が無い場合はすべての例外処理を引数として受け取ることができる。
また、以下のようにControllerにて、HttpExceptionFilterをcreate関数に関連付ける(バインディング)することができる。@UseFiltersは@Catchと処理が似ていて、引数としてFilterのインスタンスを受け取る。
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
上記のコードでは、HttpExceptionFilterが単一のメソッドに対してスコープされるが、controller/resolver/gatewayなどより広い範囲に対しても、UseFiltersを使って、スコープすることができる。
例えば以下では、グローバススコープとしてHttpExceptionFilterを定義している。
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
上記を定義することで、すべてのNestJSのファイルで記述されている例外処理(HttpException)に関して、HttpExceptionFilterがバインディングされる。
単一のメソッドに適応するのではなく、グローバルスコープを、広い範囲に対してExceptionFilterを定義するメリットとしては、各例外処理において、UseFilterを定義する必要が手間が省けることがあげられる。
Discussion