web-dev-qa-db-ja.com

nestjsエラー処理アプローチとは何ですか(ビジネスロジックエラーとhttpエラー)

NestJSを使用してAPIを作成しているときに、エラー/例外を処理するための最良の方法はどれかと思いました。私は2つの異なるアプローチを見つけました:

  1. 個別のサービスと検証パイプthrow new Error()を用意し、コントローラーにcatchを割り当て、適切な種類のHttpExceptionBadRequestExceptionForbiddenExceptionなどをスローします。)
  2. コントローラーに、ビジネスロジックのその部分の処理を担当するサービス/検証パイプメソッドを呼び出し、適切なHttpExceptionをスローさせるだけです。

両方のアプローチには長所と短所があります。

  1. これは正しいように見えますが、サービスはさまざまな理由でErrorを返すことができます。対応する種類のHttpExceptionを返すコントローラーからどのようにして知ることができますか?
  2. 非常に柔軟ですが、サービスにHttp関連のものを含めるのは間違っているようです。

私はそれを行うための「ネストjs」方法(もしあれば)はどれですか?

この問題をどのように処理しますか?

7
Aaron Ullal

ビジネスロジックがEntityNotFoundErrorをスローし、それをNotFoundExceptionにマップするとします。

そのために、エラーを変換する Interceptor を作成できます。

_@Injectable()
export class NotFoundInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // next.handle() is an Observable of the controller's result value
    return next.handle()
      .pipe(catchError(error => {
        if (error instanceof EntityNotFoundError) {
          throw new NotFoundException(error.message);
        } else {
          throw error;
        }
      }));
  }
}
_

次に、コントローラのクラスまたはメソッドに@UseInterceptors(NotFoundInterceptor)を追加することにより、それを使用できます。またはすべてのルートのグローバルインターセプターとしても。もちろん、1つのインターセプターで複数のエラーをマップすることもできます。

codesandbox で試してみてください。

2
Kim Kern

HTTPインターフェースだけでなく、GraphQLまたはその他のインターフェースにもサービスをバインドしたい場合があります。したがって、コントローラーでビジネスロジックレベルの例外をサービスからHttpレベルの例外(BadRequestException、ForbiddenException)にキャストすることをお勧めします。

簡単に言うと次のようになります

import { BadRequestException, Injectable } from '@nestjs/common';

@Injectable()
export class HttpHelperService {
  async transformExceptions(action: Promise<any>): Promise<any> {
    try {
      return await action;
    } catch (error) {
      if (error.name === 'QueryFailedError') {
        if (/^duplicate key value violates unique constraint/.test(error.message)) {
          throw new BadRequestException(error.detail);
        } else if (/violates foreign key constraint/.test(error.message)) {
          throw new BadRequestException(error.detail);
        } else {
          throw error;
        }
      } else {
        throw error;
      }
    }
  }
}

その後

1