Jak używać filtrów wyjątków Nest.js do obsługi błędów

Photo of author

By maciekx

W systemie NestJS, filtry wyjątków pełnią kluczową rolę, umożliwiając przechwytywanie i zarządzanie błędami na poziomie globalnym lub w obrębie konkretnych kontrolerów.

Dzięki nim możliwe jest scentralizowanie logiki zarządzania błędami, ustalenie jednolitego formatu odpowiedzi w przypadku wystąpienia błędów oraz zapewnienie spójności w obsłudze błędów w całej strukturze aplikacji. Zrozumienie działania i wykorzystania filtrów wyjątków jest fundamentalne dla efektywnego zarządzania błędami w twojej aplikacji.

Standardowa obsługa błędów w NestJS

NestJS domyślnie oferuje mechanizm obsługi wyjątków, który interweniuje w przypadku błędów, które nie zostały obsłużone przez kod aplikacji.

Gdy w aplikacji pojawi się nieobsłużony błąd, NestJS przejmuje kontrolę i przekazuje do klienta odpowiedź z kodem błędu serwera wewnętrznego 500. Standardowy format odpowiedzi JSON w takiej sytuacji wygląda następująco:

{
  "statusCode": 500,
  "message": "Internal server error"
}

Jeżeli obiekt błędu, zgłoszony przez Twój kod, zawiera pola `statusCode` i `message`, NestJS wykorzysta te wartości, zamiast prezentować domyślną odpowiedź.

Aby uniknąć prezentowania standardowego komunikatu o błędzie i dostarczyć klientowi bardziej zrozumiałą odpowiedź, konieczne jest odpowiednie zdefiniowanie obsługi potencjalnych błędów w aplikacji. Można to zrobić, korzystając z wbudowanych lub niestandardowych filtrów wyjątków w NestJS.

Tworzenie spersonalizowanego filtra wyjątków

Aby zademonstrować proces tworzenia filtra wyjątków, spróbujemy zbudować filtr, który będzie obsługiwał wszystkie wyjątki HTTP.

Rozpocznijmy od utworzenia pliku o nazwie `http.exception.ts` i dodania do niego poniższych importów:

import {
  ExceptionFilter,
  Catch,
  ArgumentsHost,
  HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';

Każdy z importów pełni określoną funkcję:

  • `ExceptionFilter`: Jest to interfejs określający strukturę filtra wyjątków.
  • `Catch`: To dekorator, który identyfikuje klasę jako filtr wyjątków NestJS.
  • `ArgumentsHost`: Ten interfejs pozwala na pobranie argumentów przekazanych do obsługi zdarzenia. Pozwala na wybranie odpowiedniego kontekstu (np. HTTP, RPC czy WebSockets), z którego mają zostać pobrane argumenty.
  • `HttpException`: Klasa definiująca podstawowy wyjątek HTTP w NestJS.
  • `Request` i `Response`: Interfejsy obiektów żądania i odpowiedzi z Express.js.

Następnie, stwórz klasę `HttpExceptionFilter`, która będzie implementować interfejs `ExceptionFilter`. Dodaj dekorator `Catch`, aby zasygnalizować, że klasa obsługuje wyjątki typu `HttpException`:

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {}

Wypełnij klasę następującym kodem:

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,
    message:
      exception.message
      || exception.getResponse()['message']
      || 'Internal Server Error',
  });
}

Powyższy kod pobiera obiekty żądania i odpowiedzi z `ArgumentsHost` i wyciąga szczegółowe informacje z obiektu wyjątku. Zwraca klientowi odpowiedź w formacie JSON, która zawiera szczegółowe dane na temat błędu.

Wiązanie filtrów wyjątków

Filtr wyjątków można powiązać z konkretnym kontrolerem lub z całą aplikacją, w zależności od potrzeb.

Aby powiązać filtr wyjątków globalnie, zaimportuj go do pliku `main.ts`. Następnie przekaż instancję filtra do metody `app.useGlobalFilters`:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './exception/http.exception';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(4050);
}

bootstrap();

Aby powiązać filtr wyjątków z konkretnym kontrolerem, zaimportuj dekorator `UseFilters` oraz sam filtr. Dodaj dekorator `@UseFilters` do klasy kontrolera, przekazując instancję filtra jako argument:

@Controller()
@UseFilters(new HttpExceptionFilter())
export class AppController {}

Zakres obsługi błędów zależy od miejsca powiązania filtra. Filtry powiązane z kontrolerem będą obsługiwały tylko błędy w tym kontrolerze, natomiast filtry powiązane globalnie, będą obsługiwały błędy w całej aplikacji.

Wykorzystanie wbudowanych wyjątków do zgłaszania błędów

NestJS udostępnia zestaw wbudowanych klas wyjątków, które można wykorzystać do zgłaszania błędów.

Przykładowo, do zgłaszania błędów z kodem statusu 404, można użyć klasy `NotFoundException`:

getUserById(id: number) {
  const user = users.find((user) => user.id === id);
  if (!user) {
    throw new NotFoundException({
      message: `User with id ${id} not found`,
    });
  }
}

Powyższy kod używa instrukcji warunkowej do sprawdzenia, czy użytkownik o podanym `id` istnieje. Jeśli nie, generowany jest błąd 404 za pomocą wyjątku `NotFoundException`, do którego jako argument przekazywana jest wiadomość z informacją o błędzie.

Najczęściej wykorzystywane wbudowane klasy wyjątków

Wśród innych wbudowanych klas wyjątków znajdują się między innymi:

  • `BadRequestException`: Generuje wyjątek sygnalizujący nieprawidłowe żądanie z kodem stanu 400. Używany, gdy żądanie klienta jest niepoprawne lub źle sformułowane.
  • `UnauthorizedException`: Generuje wyjątek sygnalizujący nieautoryzowany dostęp z kodem stanu 401. Używany, gdy użytkownik nie jest uwierzytelniony lub nie ma uprawnień dostępu do zasobu.
  • `ForbiddenException`: Generuje wyjątek sygnalizujący zabroniony dostęp z kodem stanu 403. Używany, gdy użytkownik jest uwierzytelniony, ale nie ma uprawnień do wykonania konkretnej operacji.
  • `RequestTimeoutException`: Generuje wyjątek sygnalizujący przekroczenie czasu żądania z kodem stanu 408. Używany, gdy serwer przerywa przetwarzanie żądania, ponieważ trwało ono zbyt długo.
  • `ConfliktException`: Generuje wyjątek sygnalizujący konflikt z kodem stanu 409. Używany, gdy żądanie klienta jest niezgodne z obecnym stanem zasobu (np. próba utworzenia istniejącego zasobu).
  • `InternalServerErrorException`: Generuje wyjątek sygnalizujący wewnętrzny błąd serwera z kodem stanu 500. Używany, gdy po stronie serwera wystąpił nieprzewidziany błąd.

Dobre praktyki obsługi błędów w NestJS

Podczas obsługi błędów w NestJS należy wykorzystywać filtry wyjątków do przechwytywania i obsługi wyjątków na poziomie globalnym lub w obrębie kontrolerów. Dodatkowo warto tworzyć spersonalizowane filtry do obsługi konkretnych typów wyjątków.

Ponadto, ważne jest, aby wykorzystywać odpowiednie wbudowane klasy wyjątków do zgłaszania trafnych i czytelnych błędów. Stosowanie tych praktyk znacznie podnosi niezawodność aplikacji NestJS.


newsblog.pl