Як використовувати фільтри винятків Nest.js для обробки помилок

У Nest.js, фільтри винятків відіграють ключову роль у перехопленні та обробці помилок, дозволяючи це робити на рівні всієї програми або окремих контролерів.

Завдяки їх використанню, логіка обробки помилок стає централізованою, а відповіді про помилки можуть бути чітко форматовані, забезпечуючи однорідний підхід до обробки помилок у вашому застосунку. Давайте розглянемо, що таке фільтри винятків та як їх ефективно застосовувати для обробки помилок у ваших проєктах.

Обробка помилок у Nest.js за замовчуванням

У Nest.js існує стандартний механізм обробки винятків, який вступає в дію, коли помилка не оброблена безпосередньо у коді вашої програми.

Коли виникає необроблена помилка, Nest.js автоматично її виявляє та повертає клієнту відповідь з кодом статусу 500, що означає внутрішню помилку сервера. Ця відповідь за замовчуванням у форматі JSON має такий вигляд:

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

Якщо об’єкт помилки, згенерований вашим кодом, вже містить властивості `statusCode` та `message`, Nest.js поверне саме ці значення, а не стандартну відповідь.

Щоб уникнути таких загальних відповідей та надати клієнту більш інформативну відповідь про помилку, необхідно ретельно обробляти всі потенційні помилки у вашому застосунку. Це можна зробити, використовуючи як вбудовані, так і кастомні фільтри винятків Nest.js.

Створення власного фільтра винятків

Давайте на прикладі розглянемо, як створити користувацький фільтр винятків, який буде обробляти всі HTTP винятки.

Спершу створіть файл `http.exception.ts` і додайте до нього наступні імпорти:

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

Ці імпорти слугують наступним цілям:

  • `ExceptionFilter`: Це інтерфейс, який визначає структуру фільтра винятків.
  • `Catch`: Цей декоратор відмічає клас як фільтр винятків Nest.
  • `ArgumentsHost`: Надає методи для отримання аргументів, переданих обробнику, дозволяючи визначати контекст виконання (наприклад, HTTP, RPC або WebSockets).
  • `HttpException`: Базовий клас для HTTP винятків у Nest.js.
  • `Request` і `Response`: Інтерфейси для об’єктів запиту та відповіді Express.js.

Створіть клас `HttpExceptionFilter`, який реалізує інтерфейс `ExceptionFilter`. Використайте декоратор `Catch` для вказівки, що цей фільтр оброблятиме винятки типу `HttpException`:

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

Цей код отримує об’єкти запиту та відповіді з `ArgumentsHost` та витягує необхідну інформацію з винятку. Він повертає клієнту JSON-відповідь зі структурованою інформацією про помилку.

Прив’язка фільтрів винятків

Ви можете застосовувати фільтри винятків до контролера або всієї програми, залежно від ваших потреб.

Щоб глобально застосувати фільтр, спочатку імпортуйте його у файл `main.ts`. Потім передайте екземпляр вашого фільтра до методу `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();

Для застосування фільтра до контролера, імпортуйте декоратор `UseFilters` та ваш фільтр. Позначте клас контролера декоратором `@UseFilters` і передайте екземпляр вашого фільтра як аргумент:

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

Область дії фільтра визначається тим, де ви його застосовуєте. Фільтри, прив’язані до контролера, діють тільки на цей контролер, а фільтри, прив’язані до всієї програми, працюють у всій програмі.

Використання вбудованих винятків для генерування помилок

Nest.js надає набір вбудованих класів винятків, які можна використовувати для викидання помилок.

Наприклад, для створення помилки 404 можна скористатися класом `NotFoundException`:

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

Цей код перевіряє, чи існує користувач з заданим `id`. Якщо користувача не знайдено, викидається помилка 404 за допомогою `NotFoundException` з відповідним повідомленням.

Поширені вбудовані класи винятків

Інші корисні вбудовані класи винятків включають, але не обмежуються, наступними:

  • `BadRequestException`: Генерує виняток “неправильний запит” з кодом статусу 400. Використовується, коли запит клієнта недійсний.
  • `UnauthorizedException`: Виняток “неавторизований” з кодом 401. Використовується, коли користувач не пройшов автентифікацію.
  • `ForbiddenException`: Виняток “заборонений” з кодом 403. Користувач автентифікований, але не має дозволу на дію.
  • `RequestTimeoutException`: Виняток “час очікування запиту минув” з кодом 408. Запит зайняв занадто багато часу.
  • `ConflictException`: Виняток “конфлікт” з кодом 409. Виникає конфлікт між запитом та поточним станом ресурсу.
  • `InternalServerErrorException`: Виняток “внутрішня помилка сервера” з кодом 500. Виникає неочікувана помилка на стороні сервера.

Найкращі практики обробки помилок у Nest.js

Для ефективної обробки помилок у Nest.js важливо використовувати фільтри винятків, щоб перехоплювати та обробляти помилки на глобальному рівні або на рівні окремих контролерів. Також можна створювати кастомні фільтри для конкретних видів помилок.

Обов’язково використовуйте відповідні вбудовані класи винятків для генерації правильних та змістовних повідомлень про помилки. Ці підходи допоможуть суттєво підвищити надійність ваших застосунків Nest.js.