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

Что такое фильтры исключений
Фильтр исключений — компонент, который перехватывает исключения в точке выхода из обработчика и формирует ответ клиенту. Коротко: фильтр получает исключение и аргументы выполнения, и решает, как вернуть ошибку.
Определение в одну строку: ExceptionFilter — интерфейс с методом catch(exception, host), где host даёт доступ к Request/Response.
Важно: фильтры не заменяют валидацию, авторизацию или структуру данных — они служат для управления ошибками, логирования и приведения ответа к единому формату.
Стандартная обработка ошибок в Nest.js
По умолчанию Nest.js ловит необработанные исключения и возвращает 500 Internal Server Error. Типичный ответ выглядит так:
{
"statusCode": 500,
"message": "Internal server error"
}Если брошенное исключение содержит statusCode и message (например, стандартные HttpException-классы), Nest.js использует их вместо дефолтного ответа.
Чтобы заменить поведение по умолчанию и дать клиенту более осмысленный ответ, используйте встроенные или собственные фильтры исключений.
Создание кастомного фильтра исключений
Пример: фильтр, обрабатывающий все HttpException.
Создайте файл 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.
Добавьте класс, помеченный декоратором Catch:
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {}Заполните метод catch этим кодом:
catch(exception: HttpException, host: ArgumentsHost) {
// Получаем HTTP-контекст и объекты request/response
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',
});
} Этот фильтр извлекает request/response из ArgumentsHost и формирует единый JSON с полями statusCode, timestamp, path и message.
Важно: не выводите в production полные стек-трейсы в ответах — логируйте детали, но отправляйте клиенту безопасное сообщение.
Привязка фильтров
Можно привязывать фильтры на уровне приложения или контроллера.
Чтобы зарегистрировать глобально, импортируйте фильтр в main.ts и вызовите app.useGlobalFilters:
// 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();Чтобы привязать к конкретному контроллеру, используйте декоратор @UseFilters:
@Controller()
@UseFilters(new HttpExceptionFilter())
export class AppController {}Scope влияет на объём покрытия: глобальный фильтр обслуживает все контроллеры и middleware-цепочки, тогда как фильтр, привязанный к контроллеру, действует только для вызовов внутри этого контроллера.
Использование встроенных исключений для генерации ошибок
Nest.js предоставляет набор готовых классов исключений, которые устанавливают соответствующие HTTP-статусы.
Пример: выбрасываем 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`,
});
}
}Список часто используемых встроенных классов:
- BadRequestException — 400. Используйте при неверном запросе или ошибках валидации.
- UnauthorizedException — 401. Когда пользователь не аутентифицирован.
- ForbiddenException — 403. Когда пользователь аутентифицирован, но не авторизован.
- NotFoundException — 404. Ресурс не найден.
- RequestTimeoutException — 408. Запрос превысил время ожидания.
- ConflictException — 409. Конфликт состояния, например, дублирование ресурса.
- InternalServerErrorException — 500. Внутренняя ошибка сервера.
Использование встроенных классов упрощает создание предсказуемых ответов и интеграцию с фильтрами, которые опираются на getStatus().
Лучшие практики обработки ошибок
- Централизация: держите общий формат ответов в глобальном фильтре.
- Разделение обязанностей: валидация — пайпы, авторизация — гварды, а ошибки — фильтры.
- Не проливайте детали в клиентские ответы: оставляйте стек и чувствительные данные в логах.
- Корреляция: добавляйте correlationId (X-Request-Id) и возвращайте его в ответе для отладки.
- Логирование: логируйте исключения с уровнем и метаданными (маршрут, пользователь, id запроса).
- Мониторинг: интегрируйте Sentry/Datadog для трекинга ошибок и алертов.
- Тестируемость: покрывайте фильтры юнит-тестами и интеграционными тестами, проверяйте формат ответа для каждого типа исключения.
- Локализация: если API ориентирован на пользователей, локализуйте сообщения ошибок на нужные языки.
Альтернативные подходы и когда использовать их
- Interceptors (перехватчики): полезны для преобразования успешных ответов и глобальной трансформации, но не заменяют фильтры при обработке исключений.
- Middleware: подходит для логирования и добавления заголовков, но они работают до контроллера и не всегда видят исключения, возникающие позже.
- Pipes: оптимальны для валидации/трансформации данных до контроллера — бросают BadRequestException при ошибках валидации.
- Guards: блокируют доступ к эндпойнтам (401/403) — используются вместе с фильтрами для возврата корректных ошибок.
Когда фильтры не помогут:
- Ошибки на уровне инфраструктуры (база данных, сеть) требуют ретрайев и fallback-логики, которые реализуются в сервисах/интеграциях.
- Если вы хотите изменять успешный результат (2xx), используйте Interceptors.
Когда кастомный фильтр может навредить
- Если вы теряете оригинальную семантику исключения (заменяете полезный статус на 500).
- Если фильтр пробрасывает приватные данные в ответе.
- Если фильтр слишком тяжёлый и выполняет много синхронной работы (может замедлить отклики).
Руководство по внедрению (мини-методология)
- Определите формат общего ответа об ошибке (поля: statusCode, message, timestamp, path, correlationId).
- Реализуйте глобальный HttpExceptionFilter и логирование исключений.
- Добавьте в pipeline middleware генерацию correlationId.
- Покройте юнит-тестами поведение фильтра для разных типов исключений.
- Интегрируйте мониторинг и алерты (Sentry/Prometheus).
- Проведите нагрузочное тестирование, чтобы убедиться, что фильтр не становится узким местом.
Чеклист ролей
Для разработчика:
- Реализован общий формат ошибок
- Написаны юнит-тесты для фильтра
- Не возвращаются стек-трейсы в production
Для QA:
- Проверены ответы на 400/401/403/404/500
- Проверена корреляция request-id в логах и ответах
Для оператора/DevOps:
- Настроены алерты на увеличение числа 5xx
- Интеграция с Sentry/Tracing настроена
Фактбокс — ключевые статусы
- 400 — Bad Request
- 401 — Unauthorized
- 403 — Forbidden
- 404 — Not Found
- 408 — Request Timeout
- 409 — Conflict
- 500 — Internal Server Error
Критерии приёмки
- API возвращает предопределённый JSON-формат ошибок для всех проверенных сценариев.
- Логи содержат correlationId и достаточные метаданные для отладки.
- Никакие чувствительные данные не возвращаются в ответах клиентов.
- Юнит- и интеграционные тесты покрывают основные исключения.
Примеры тестовых сценариев и критерии приёмки
- Запрос с невалидным телом должен вернуть 400 и корректный JSON-ответ.
- Доступ без токена к защищённому маршруту должен вернуть 401.
- Запрос к отсутствующему ресурсу должен вернуть 404 с понятным message.
- Исключение в сервисе (необработанное) должно логироваться и возвращать 500 без приватных данных.
Примеры отказа и варианты обхода
- Если клиент требует детальных ошибок для отладки, предоставьте режим “debug” только для локальной среды и не включайте его в production.
- Если приложение использует GraphQL, используйте соответствующие фильтры исключений для GraphQL-контекста — HTTP-фильтры не всегда применимы.
Безопасность и приватность
Не возвращайте в ответах внутренние сообщения ошибок, стек-трейсы или данные конфигурации. Логируйте чувствительные данные в защищённом хранилище с ограниченным доступом. При работе с персональными данными соблюдайте требования GDPR/локального законодательства: храните минимальный набор логов и маскируйте персональные идентификаторы в ошибках.
Короткий глоссарий
- ExceptionFilter: интерфейс для фильтров исключений.
- ArgumentsHost: объект, дающий доступ к контексту исполнения.
- HttpException: базовый класс для HTTP-исключений в Nest.js.
- Interceptor: компонент для перехвата и трансформации ответов.
Decision flowchart
flowchart TD
A[Запрос пришёл] --> B{Ошибка возникла в контроллере?}
B -- Нет --> C[Возврат успешного ответа]
B -- Да --> D{Это HttpException?}
D -- Да --> E[Пойман HttpExceptionFilter]
D -- Нет --> F[Пойман глобальным фильтром как 500]
E --> G[Логирование, ответ с кодом и message]
F --> G
G --> H[Мониторинг/Алерт]Заключение
Фильтры исключений — ключевой инструмент в Nest.js для согласованной, безопасной и наблюдаемой обработки ошибок. Комбинируйте их с пайпами, гвардами и инструментами мониторинга, следите за тем, чтобы клиентам возвращались информативные, но безопасные сообщения. Правильно реализованная обработка ошибок снижает время на диагностику и повышает надёжность системы.
Похожие материалы
Взлом Wi‑Fi: методы и защита
Суммирование в Google Sheets — числа, ячейки, матрицы
Разблокировка iPhone с Apple Watch
Импорт и экспорт данных в Excel
Автоматизация Android для длительной работы батареи