2022年12月14日

NestJS と Prisma で REST API を構築する:エラー処理

6 分で読めます

NestJS、Prisma、PostgreSQL で REST API を構築するシリーズの第 3 回チュートリアルへようこそ!このチュートリアルでは、NestJS アプリケーションでエラー処理を実行する方法を学びます。

Building a REST API with NestJS and Prisma: Error Handling

目次

はじめに

このシリーズの最初の章では、新しい NestJS プロジェクトを作成し、Prisma、PostgreSQL、Swagger と統合しました。次に、ブログアプリケーションのバックエンド用の基本的な REST API を構築しました。第 2 章では、入力の検証と変換を行う方法を学びました。

この章では、NestJS でエラーを処理する方法を学びます。2 つの異なる戦略を見ていきます。

  1. まず、API のコントローラー内でアプリケーションコードのエラーを直接検出してスローする方法を学びます。
  2. 次に、例外フィルタを使用して、アプリケーション全体で処理されない例外を処理する方法を学びます。

このチュートリアルでは、最初の章で構築した REST API を使用します。このチュートリアルに従うために、第 2 章を完了する必要はありません。

開発環境

このチュートリアルを進めるには、以下が必要です。

  • ... Node.js がインストールされていること。
  • ... DockerDocker Compose がインストールされていること。Linux を使用している場合は、Docker のバージョンが 20.10.0 以降であることを確認してください。ターミナルで docker version を実行して Docker のバージョンを確認できます。
  • ... オプションで Prisma VS Code Extension がインストールされていること。Prisma VS Code 拡張機能は、Prisma に非常に優れた IntelliSense と構文の強調表示を追加します。
  • ... オプションで 、このシリーズで提供されるコマンドを実行するために、Unix シェル (Linux および macOS のターミナル/シェルなど) にアクセスできること。

Unix シェルがない場合 (たとえば、Windows マシンを使用している場合) でも、チュートリアルを進めることはできますが、シェルコマンドをマシンに合わせて変更する必要がある場合があります。

リポジトリのクローン

このチュートリアルの開始点は、このシリーズのパート 1の最後です。これには、NestJS で構築された基本的な REST API が含まれています。

このチュートリアルの開始点は、end-rest-api-part-1 ブランチの GitHub リポジトリにあります。開始するには、リポジトリをクローンして、end-rest-api-part-1 ブランチをチェックアウトします。

開始するには、次のアクションを実行します。

  1. クローンしたディレクトリに移動します。
  1. 依存関係をインストールします。
  1. Docker で PostgreSQL データベースを起動します。
  1. データベースの移行を適用します。
  1. プロジェクトを開始します。

注意:ステップ 4 では、Prisma Client も生成され、データベースにシードが設定されます。

これで、http://localhost:3000/api/ で API ドキュメントにアクセスできるようになります。

プロジェクトの構造とファイル

クローンしたリポジトリの構造は次のようになります。

このリポジトリの注目すべきファイルとディレクトリは次のとおりです。

  • src ディレクトリには、アプリケーションのソースコードが含まれています。3 つのモジュールがあります。
    • app モジュールは、src ディレクトリのルートにあり、アプリケーションのエントリポイントです。これは、Web サーバーを起動する役割を担います。
    • prisma モジュールには、データベースへのインターフェイスである Prisma Client が含まれています。
    • articles モジュールは、/articles ルートのエンドポイントと付随するビジネスロジックを定義します。
  • prisma モジュールには、以下が含まれています。
    • schema.prisma ファイルは、データベーススキーマを定義します。
    • migrations ディレクトリには、データベース移行履歴が含まれています。
    • seed.ts ファイルには、開発データベースにダミーデータをシードするためのスクリプトが含まれています。
  • docker-compose.yml ファイルは、PostgreSQL データベースの Docker イメージを定義します。
  • .env ファイルには、PostgreSQL データベースのデータベース接続文字列が含まれています。

注意:これらのコンポーネントの詳細については、このチュートリアルシリーズのパート 1を参照してください。

例外を直接検出してスローする

このセクションでは、アプリケーションコードで例外を直接スローする方法を説明します。GET /articles/:id エンドポイントの問題に対処します。現在、存在しない id 値をこのエンドポイントに提供すると、エラーではなく HTTP 200 ステータスで何も返されません。

たとえば、GET /articles/234235 リクエストを試してください。

Requesting an article that does not exist returns HTTP 200

これを修正するには、articles.controller.tsfindOne メソッドを変更する必要があります。記事が存在しない場合は、NestJS によって提供される組み込み例外である NotFoundException をスローします。

articles.controller.tsfindOne メソッドを更新します。

同じリクエストを再度行うと、ユーザーフレンドリーなエラーメッセージが表示されるはずです。

Requesting an article that does not exist returns HTTP 404

例外フィルタを使用して例外を処理する

専用の例外レイヤーの利点

前のセクションでは、エラー状態を検出し、手動で例外をスローしました。多くの場合、例外はアプリケーションコードによって自動的に生成されます。そのような場合は、例外を処理し、適切な HTTP エラーをユーザーに返す必要があります。

各コントローラーで例外をケースバイケースで手動で処理することも可能ですが、多くの理由から良い考えではありません。

  • コアアプリケーションロジックが、多くのエラー処理コードで煩雑になります。
  • エンドポイントの多くは、リソースが見つからないなど、同様のエラーを処理します。同じエラー処理コードを多くの場所で複製する必要があります。
  • エラー処理ロジックが多くの場所に分散しているため、変更が困難になります。

これらの問題を解決するために、NestJS には、アプリケーション全体で処理されない例外を処理する責任を負う例外レイヤーがあります。NestJS では、アプリケーション内でスローされるさまざまな種類の例外を処理する方法を定義する例外フィルタを作成できます。

NestJS グローバル例外フィルタ

NestJS には、処理されないすべての例外をキャッチするグローバル例外フィルタがあります。グローバル例外フィルタを理解するために、例を見てみましょう。次の本文を使用して、POST /articles エンドポイントに2 つのリクエストを送信します。

最初の要求は成功しますが、2 番目の要求は、同じ title フィールドを持つ記事を既に作成しているため失敗します。次のエラーが表示されます。

NestJS サーバーを実行しているターミナルウィンドウを見ると、次のエラーが表示されるはずです。

ログから、Prisma スキーマで @unique としてマークされている title フィールドが原因で、Prisma Client が一意制約検証エラーをスローしていることがわかります。例外の型は PrismaClientKnownRequestError であり、Prisma 名前空間レベルでエクスポートされます。

PrismaClientKnownRequestError はアプリケーションによって直接処理されていないため、組み込みのグローバル例外フィルタによって自動的に処理されます。このフィルタは、HTTP 500 「内部サーバーエラー」応答を生成します。

手動例外フィルタを作成する

このセクションでは、表示された PrismaClientKnownRequestError を処理するためのカスタム例外フィルタを作成します。このフィルタは、型 PrismaClientKnownRequestError のすべての例外をキャッチし、明確なユーザーフレンドリーなエラーメッセージをユーザーに返します。

Nest CLI を使用してフィルタクラスを生成することから始めます。

これにより、次の内容で新しいファイル src/prisma-client-exception.filter.ts が作成されます。

注意:テストを作成するための src/prisma-client-exception.filter.spec.ts という 2 番目のファイルが作成されています。このファイルは今のところ無視できます。

catch メソッドが空であるため、eslint からエラーが発生します。PrismaClientExceptionFiltercatch メソッドの実装を次のように更新します。

ここでは、次の変更を行いました。

  1. このフィルタが型 PrismaClientKnownRequestError の例外をキャッチするようにするために、@Catch デコレータに追加しました。
  2. 例外フィルタは、NestJS コアパッケージの BaseExceptionFilter クラスを拡張します。このクラスは、ユーザーに「内部サーバーエラー」応答を返す catch メソッドのデフォルト実装を提供します。詳細については、NestJS ドキュメントを参照してください。
  3. エラーメッセージをコンソールに記録するために、console.error ステートメントを追加しました。これは、デバッグの目的で役立ちます。

Prisma は、さまざまな種類のエラーに対して PrismaClientKnownRequestError をスローします。したがって、PrismaClientKnownRequestError 例外からエラーコードを抽出する方法を理解する必要があります。PrismaClientKnownRequestError 例外には、エラーコードを含む code プロパティがあります。Prisma エラーメッセージリファレンスでエラーコードのリストを見つけることができます。

探しているエラーコードは P2002 で、一意制約違反の場合に発生します。エラーが発生した場合に HTTP 409 Conflict 応答をスローするように catch メソッドを更新します。また、カスタムエラーメッセージをユーザーに提供します。

次のように例外フィルタの実装を更新します。

ここでは、基盤となるフレームワーク Response オブジェクトにアクセスし、応答を直接変更しています。デフォルトでは、express は NestJS の下で使用される HTTP フレームワークです。P2002 以外の例外コードについては、デフォルトの「内部サーバーエラー」応答を送信しています。

注意:本番アプリケーションでは、エラーメッセージでユーザーに機密情報を漏洩しないように注意してください。

例外フィルタをアプリケーションに適用する

次に、PrismaClientExceptionFilter を有効にするには、特定のスコープに適用する必要があります。例外フィルタは、個々のルート (メソッドスコープ)、コントローラー全体 (コントローラースコープ)、またはアプリケーション全体 (グローバルスコープ) にスコープできます。

main.ts ファイルを更新して、例外フィルタをアプリケーション全体に適用します。

次に、POST /articles エンドポイントに同じリクエストを送信してみてください。

今回は、よりユーザーフレンドリーなエラーメッセージが表示されます。

PrismaClientExceptionFilter はグローバルフィルタであるため、アプリケーションのすべてのルートでこの特定のエラータイプを処理できます。

例外フィルタの実装を拡張して、他のエラーも処理することをお勧めします。たとえば、データベースでレコードが見つからない場合に発生する P2025 エラーコードを処理するケースを追加できます。このエラーには、ステータスコード HttpStatus.NOT_FOUND を返す必要があります。これは、PATCH /articles/:id および DELETE /articles/:id エンドポイントに役立ちます。

ボーナス:nestjs-prisma パッケージで Prisma 例外を処理する

これまでのところ、NestJS アプリケーションで Prisma 例外を手動で処理するためのさまざまな手法を学びました。NestJS で Prisma を使用するための専用パッケージ nestjs-prisma を使用して、Prisma 例外を処理することもできます。このパッケージは、多くのボイラープレートコードを削除するため、検討するのに最適なオプションです。

パッケージのインストールと使用に関する手順は、nestjs-prisma ドキュメントで入手できます。このパッケージを使用すると、このパッケージが自動的に作成するため、個別の prisma モジュールとサービスを手動で作成する必要はありません。

ドキュメントの例外フィルタセクションで、パッケージを使用して Prisma 例外を処理する方法を学ぶことができます。このチュートリアルの今後の章では、nestjs-prisma パッケージについて詳しく説明します。

まとめと最終的な感想

おめでとうございます!このチュートリアルの既存の NestJS アプリケーションを取り上げ、エラー処理を統合する方法を学びました。アプリケーションコードで直接エラーを処理する方法と、例外フィルタを作成する方法の 2 つの異なる方法を学びました。

この章では、Prisma エラーを処理する方法を学びました。ただし、手法自体は Prisma に限定されません。これらを使用して、アプリケーション内の任意のエラータイプを処理できます。

このチュートリアルの完成したコードは、end-error-handling-part-3 ブランチの GitHub リポジトリにあります。問題に気付いた場合は、リポジトリに issue を作成するか、PR を送信してください。また、Twitter で直接私に連絡することもできます。

次回の投稿をお見逃しなく!

Prisma ニュースレターにサインアップ