NestJS
を用いAPI作成をしているのだが、Response
を共通化したいと思ったので、実装してみた。
Responseの定石
とは言えそもそもResponse
の形をどのようにするのがいいのかわからなかったので調べた結果、omniti-labs/jsendが参考になりそうだった。 これを参考に以下のような型で返すことにした。
export interface Response {
status: 'success' | 'error';
data: any;
message: string[] | null;
}
ValidationPipeを使用する場合、エラーレスポンスの形式はBadRequestExceptionがデフォルト
たぶんドキュメントにある通り(Documentation | NestJS - A progressive Node.js framework)なので、ほとんどの場合ValidationPipe
を使用することになる。 そうするとエラーレスポンスの形式はBadRequestException
がデフォルトになるので、以下のように返ってくる。
{
"statusCode": 400,
"message": "Bad Request Exception",
"error": "Bad Request"
}
これは意図した形ではないのでカスタマイズする必要がある。
http-exception.filter.tsを作成する
カスタマイズするにはそれ用のException filters
を用意する必要がある。 Exception filters
とは、
Nestには例外処理レイヤーが組み込まれており、アプリケーション全体で処理されない例外をすべて処理する責任を負っています。アプリケーションのコードで処理できない例外は、このレイヤーでキャッチされ、適切なユーザーフレンドリーな応答が自動的に送信されます。
というものらしい。
とにかく例外フィルターは、NestJSアプリケーションが例外をキャッチした場合に呼び出されるらしい。
実装は簡単
まずはhttp-exception.filter.ts
を作成する。
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import { 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 status = exception.getStatus();
response.status(status).json({
status: 'error',
data: null,
message: (exception.getResponse() as any).message || [
'何らかのエラーが発生しました。',
],
});
}
}
それをmain.ts
で読み込むだけで、
app.useGlobalPipes(new ValidationPipe());
+ app.useGlobalFilters(new HttpExceptionFilter());
エラーレスポンスが期待した形になって返却されるようになった。
余談
これを実装するときに脳死でChatGPT
に質問したところ、いくら質問してもうまく実装できず、ドキュメントを見たら即解決した。 馬鹿と鋏は使いようということで。