Гид по технологиям

Обработка ошибок в Nest.js: фильтры исключений

6 min read Backend Обновлено 09 Jan 2026
Обработка ошибок в Nest.js: фильтры исключений
Обработка ошибок в Nest.js: фильтры исключений

Логотип Nest.js: красный силуэт головы кошки на фоне материнской платы компьютера

Что делает фильтр исключений в Nest.js

Фильтр исключений (Exception Filter) перехватывает ошибки, брошенные в обработчиках маршрутов, и формирует ответ клиенту. Он может работать на уровне контроллера или глобально для всего приложения.

Ключевая идея: фильтр отделяет бизнес-логику от логики отображения ошибок. Это упрощает поддержку, тестирование и согласованность ответов API.

Важно: фильтры не заменяют проверку входных данных — используйте Pipes и DTO для валидации.

Стандартное поведение Nest.js при ошибках

По умолчанию Nest.js перехватывает необработанные исключения и возвращает ответ 500 Internal Server Error с простым JSON:

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

Если вы кидаете объект ошибки, у которого есть statusCode и message, Nest вернёт их вместо ответа по умолчанию. Но лучше явно контролировать ответы через фильтры и встроенные исключения.

Создание собственного фильтра исключений

Ниже — пример фильтра, который обрабатывает HttpException и формирует детализированный JSON для клиента.

Создайте файл http.exception.ts и добавьте импорты:

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

import { Request, Response } from 'express';

Назначение импортов кратко:

  • ExceptionFilter — интерфейс для фильтра исключений.
  • Catch — декоратор, отмечающий класс как фильтр для указанных типов исключений.
  • ArgumentsHost — даёт доступ к аргументам обработчика и контекстам (HTTP, RPC, WebSockets).
  • HttpException — базовый класс HTTP-исключений Nest.
  • Request/Response — интерфейсы Express.

Создайте класс, реализующий ExceptionFilter, и пометьте его декоратором Catch:

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

Заполните класс логикой:

catch(exception: HttpException, host: ArgumentsHost) {
  // Получаем HTTP-контекст и объекты запроса/ответа
  const ctx = host.switchToHttp();
  const response = ctx.getResponse();
  const request = ctx.getRequest();

  // Получаем статус из исключения
  const status = exception.getStatus();

  // Формируем структурированный JSON-ответ
  response.status(status).json({
    statusCode: status,
    timestamp: new Date().toISOString(),
    path: request.url,
    message:
      exception.message
      || (exception.getResponse() as any)['message']
      || 'Internal Server Error',
  });
}

Примечание: exception.getResponse() может вернуть объект или строку; в реальном приложении добавьте проверки типов.

Привязка фильтров: глобально и на контроллер

Глобальная привязка в main.ts:

// main.ts
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();

Привязка к контроллеру:

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

Где привязывать фильтр зависит от области покрытия: контроллерный фильтр влияет только на этот контроллер; глобальный — на всё приложение.

Важно: порядок подключений влияет — глобальные фильтры применяются к запросам, если контроллеры/роуты не переопределяют поведение локальными фильтрами.

Встроенные исключения Nest.js и как их использовать

Nest предоставляет готовые классы исключений для распространённых HTTP-статусов. Их удобно бросать в коде, чтобы вернуть корректный ответ клиенту:

Пример: NotFoundException для 404

getUserById(id: number) {
  const user = users.find((user) => user.id === id);

  if (!user) {
    throw new NotFoundException({
      message: `User with id ${id} not found`,
    });
  }
}

Общие встроенные классы:

  • BadRequestException — 400
  • UnauthorizedException — 401
  • ForbiddenException — 403
  • NotFoundException — 404
  • RequestTimeoutException — 408
  • ConflictException — 409
  • InternalServerErrorException — 500

Эти классы помогают делать ответы понятными и соответствующими REST-практикам.

Когда фильтры не решают всех задач — ограничения и контрпримеры

  • Валидация входных данных: за неё отвечают Pipes и DTO. Фильтр лишь обрабатывает уже возникшую ошибку.
  • Асинхронные фоновые задачи: ошибки внутри задач cron/queue могут не попадать в HTTP-контекст; нужны отдельные обработчики и логирование.
  • Ошибки на уровне инфраструктуры (подключение к БД, депозитории): лучше оборачивать в понятные исключения, прежде чем отдавать клиенту технические детали.

Контрпример: если в контроллере вы ловите ошибку try/catch и возвращаете кастомный ответ, фильтр не сработает — это ожидаемое поведение.

Альтернативы и дополнения к фильтрам

  • Interceptors (перехватчики): можно оборачивать цепочку обработки запроса, модифицировать ответ и ловить ошибки через RxJS catchError. Полезны для общего трансформирования ответов и метрик.
  • Middleware: выполняется до маршрута, хорош для аутентификации, логирования и раннего прерывания запроса.
  • Guard: для авторизации/аутентификации, возвращает 401/403 ещё до выполнения обработчика.
  • Pipes: валидация и трансформация входных данных.

Пример паттерна: валидация через Pipe -> Guard для доступа -> контроллер кидает исключение -> Exception Filter формирует ответ -> Interceptor добавляет стандартную обёртку и метрики.

Интеграция логирования и внешних сервисов ошибок

Фильтры — отличное место для единого логирования и интеграции с APM/отслеживанием ошибок (Sentry, Datadog и т.п.). Общая рекомендация:

  1. В фильтре логируйте минимум: тип исключения, статус, путь, идентификатор запроса.
  2. Включайте stack trace только для внутренних сред (стадия разработки или staging), а продуктивным клиентам отдавайте обобщённые сообщения.
  3. Не отправляйте в ответ чувствительные данные (пароли, токены).

Важно для соответствия GDPR: не логируйте персональные данные без маскировки и правовой основы.

Шаблон расширенного фильтра с логированием (пример)

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const status = exception.getStatus();

    // Простой лог
    console.error({
      status,
      path: request.url,
      message: exception.message,
      // stack: exception.stack // включать по необходимости
    });

    response.status(status).json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: exception.message || 'Internal Server Error',
    });
  }
}

Пошаговая мини-методология внедрения фильтров в проект

  1. Проанализируйте существующие ошибки и форматы ответов API.
  2. Определите стандартную схему ошибки (statusCode, message, timestamp, path, code).
  3. Напишите глобальный фильтр, который формирует эту схему и логирует инциденты.
  4. Покройте тестами сценарии — юнит и интеграционные (см. раздел Критерии приёмки).
  5. Для специфичных контроллеров добавьте локальные фильтры при необходимости.
  6. Интегрируйте с системой мониторинга и настройте оповещения.

Критерии приёмки

  • Все контроллеры возвращают ошибки в едином формате.
  • В логах присутствуют: timestamp, путь, статус, краткое сообщение.
  • Stack trace не доступен клиентам в продакшене.
  • Валидация входных данных происходит через Pipes и возвращает 4xx ошибки.
  • Интеграция с системой оповещений настроена для ошибок 5xx.

Ролевые чек-листы

Developer:

  • Использовать встроенные исключения для предсказуемых ошибок.
  • Не раскрывать чувствительные данные в ошибках.
  • Покрыть новые фильтры unit-тестами.

Code reviewer:

  • Проверить, что фильтр не меняет бизнес-логику.
  • Проверить корректность логирования и отсутствие PII в логах.

DevOps / SRE:

  • Настроить централизованное логирование и оповещения для 5xx.
  • Проверить, что скорость обработки ошибок не деградирует приложение.

Читабельная шпаргалка: какие исключения использовать

  • 400 — BadRequestException (ошибка клиента, неверные данные)
  • 401 — UnauthorizedException (неавторизован)
  • 403 — ForbiddenException (нет прав)
  • 404 — NotFoundException (ресурс не найден)
  • 409 — ConflictException (конфликт состояния)
  • 408 — RequestTimeoutException (тайм-аут)
  • 500 — InternalServerErrorException (внутренняя ошибка)

Пример decision flow (Mermaid)

flowchart TD
  A[Начало запроса] --> B{Валидация DTO}
  B -- Ошибка --> C[Возврат 400 через Pipe]
  B -- ОК --> D{Guard: авторизован?}
  D -- Нет --> E[Возврат 401]
  D -- Да --> F[Выполнение контроллера]
  F -- Ошибка 'брошено исключение' --> G[Exception Filter]
  G --> H[Логирование + форматированный ответ]
  F -- Успех --> I[Стандартный ответ]

Тест-кейсы и приёмка

  • Юнит: фильтр возвращает корректный statusCode и структуру JSON для HttpException.
  • Интеграция: при NotFoundException контроллер должен вернуть 404 с ожидаемым сообщением.
  • Негатив: убедиться, что приватные поля не попадают в ответ при 500.

Безопасность и приватность

  • Фильтры не должны раскрывать внутренние сообщения ошибок клиентам в продакшене.
  • Маскируйте или удаляйте персональные данные из логов.
  • При интеграции с внешними APM соблюдайте требования локального законодательства о передаче данных.

Примеры реального использования

  • Перехват ошибок в REST API и возвращение консистентной схемы ошибки для фронтенда.
  • Формирование удобных сообщений для мобильных приложений (короткие message + код для локализации).
  • Отправка критичных ошибок в Sentry с контекстом: путь, тело запроса (обрезанное), id пользователя.

Часто задаваемые вопросы

Какой уровень покрытия фильтрами предпочтителен: глобальный или локальный?

Выберите глобальный фильтр как базовый уровень (единый формат ошибок). Добавляйте локальные фильтры для специфичных контроллеров или модулей, если нужна особая логика.

Можно ли через фильтр возвращать HTML-страницу вместо JSON?

Да. Фильтр получает объект Response и может вернуть HTML, редирект или любой другой контент тип. Но для REST API рекомендуется использовать JSON.

Нужно ли логировать стек-трейс в продакшене?

Как правило, нет. Логируйте стек-трейс во внутренние мониторинговые системы, но не включайте его в ответы клиентам.

Итог

Фильтры исключений в Nest.js — мощный инструмент для централизации обработки ошибок. Комбинируйте их со встроенными исключениями, Pipes, Guards и Interceptors чтобы получить предсказуемые, безопасные и удобные для потребителей API ошибки. Начните с глобального фильтра, определите формат ответа и расширяйте решение по мере роста приложения.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство