Ответ
В NestJS запрос проходит через строго определенную цепочку (middleware -> guards -> interceptors -> pipes -> controller -> interceptors -> exception filters). Я настраивал эту цепочку для добавления логирования, авторизации и валидации.
Порядок выполнения:
- Глобальные и модульные Middleware: Идеально для логирования, CORS, сжатия данных. Они выполняются до маршрутизации.
- Guards: Проверяют права доступа на основе ролей или JWT-токена. Если guard возвращает
false, цепочка прерывается. - Interceptors (до обработчика): Могут трансформировать входящий запрос или добавлять дополнительную логику (например, логирование времени выполнения).
- Pipes: Преобразуют и валидируют данные для параметров обработчика (например, с помощью class-validator).
- Controller Handler: Выполняется целевой метод контроллера.
- Interceptors (после обработчика): Могут трансформировать исходящий ответ.
- Exception Filters: Перехватывают и обрабатывают исключения, возникшие на любом этапе.
Пример Guard и Pipe:
// Guard для проверки JWT
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization?.split(' ')[1];
if (!token) throw new UnauthorizedException();
try {
const payload = this.jwtService.verify(token);
request.user = payload; // Добавляем пользователя в запрос
return true;
} catch {
throw new UnauthorizedException('Неверный токен');
}
}
}
// Pipe для валидации DTO
@Injectable()
export class ValidationPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
const object = plainToClass(metadata.metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException('Валидация не пройдена');
}
return object;
}
}
Практическое применение: В моем последнем проекте я использовал глобальный interceptor для оборачивания всех ответов в единый формат { data: ..., timestamp: ... } и глобальный guard для проверки API-ключей на публичных эндпоинтах.
Ответ 18+ 🔞
Давай разберём эту матрёшку из NestJS, а то звучит как инструкция к стиральной машине, которую писал человек с овердохуищей свободного времени.
Смотри, представь, что запрос — это какой-нибудь Вася, который пришёл в закрытый клуб. Порядок, в котором его будут обламывать, строгий и неумолимый.
- Middleware (Глобальные и модульные). Это как швейцар у двери и фейсконтроль в одном флаконе. Вася только переступил порог, а его уже оценивают: «А, это опять ты, логируем. Разрешаем пройти дальше, но CORS тебе в сраку, чувак». Выполняется до того, как вообще поняли, в какой зал ему идти.
- Guards. Вот Вася подошёл к нужной двери (роуту). А там стоит здоровенный чувак с лицом кирпичом и спрашивает: «А у тебя есть токен? А роль подходящая?». Это Guard. Если нет — «Иди нахуй, Вася», и вся история на этом заканчивается. Авторизация, JWT — всё тут.
- Interceptors (до обработчика). Допустим, Guard пропустил. Но прежде чем Вася зайдёт в зал (контроллер), его могут ещё раз обыскать или, например, засечь время входа. Interceptor — это такой хитрожопый администратор, который может покрутить запрос в руках, что-то подкрутить, залогировать «Вася зашёл в 21:37».
- Pipes. Вася внутри, хочет заказать напиток. Но говорит: «Дайте мне аджыку». Pipe — это бармен, который его слушает и думает: «Бля, аджика — это соус. Наверное, он имел в виду «Апероль». Или вообще: «Нет, чувак, такую хуйню мы не подаём, вот тебе BadRequestException». Валидация и преобразование данных — их царство. Class-validator тут рулит.
- Controller Handler. Наконец-то! Вася дошёл до стойки, сделал заказ (вызвался метод контроллера), и ему налили его напиток (выполнили бизнес-логику).
- Interceptors (после обработчика). Вася получил свой коктейль и поворачивается уходить. А тот самый хитрожопый администратор (interceptor) снова тут как тут: «Погоди, Вася. Давай я твой стакан красиво упакую в обёртку
{ data: твой_коктейль, timestamp: ... }, а то выходить с голым стаканом — моветон». Трансформация ответа. - Exception Filters. А вот если на любом из этих этапов всё пошло по пизде — Васи нет в списке, у Васи токен просрочен, Вася заказал аджику, а бармен обозвался и швырнул в него стаканом — за всё это отвечают Exception Filters. Они ловят эти летящие нахуй исключения и говорят: «Так, народ, не кипятиться. Васе мы сейчас красиво отправим 401 или 400, чтобы он не охуел совсем».
Вот тебе примеры, как это выглядит в коде, ёпта:
// Guard — тот самый бугай на входе. Проверяет JWT.
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
// Ищем токен в заголовках. Если его нет — сразу иди нахуй.
const token = request.headers.authorization?.split(' ')[1];
if (!token) throw new UnauthorizedException();
try {
// Пробуем проверить. Не верим на слово.
const payload = this.jwtService.verify(token);
request.user = payload; // Если всё ок — прилепляем пользователя к запросу.
return true;
} catch {
// Токен кривой или просроченный? Иди нахуй, Вася, в другом месте кодируй.
throw new UnauthorizedException('Неверный токен');
}
}
}
// Pipe — бармен-педант. Валидирует DTO.
@Injectable()
export class ValidationPipe implements PipeTransform {
async transform(value: any, metadata: ArgumentMetadata) {
// Превращаем сырые данные (скорее всего, объект) в красивый класс.
const object = plainToClass(metadata.metatype, value);
// Спрашиваем у class-validator: «Ну чё, нормально?»
const errors = await validate(object);
if (errors.length > 0) {
// Если ошибок овердохуища — кидаем исключение.
throw new BadRequestException('Валидация не пройдена');
}
// Если всё чисто — пропускаем дальше, в контроллер.
return object;
}
}
А на практике, в последнем проекте, я это применял так:
Глобальный interceptor — чтобы все ответы от сервера были завёрнуты в одну обёртку, типа { data: ..., timestamp: ... }. А то фронтендеры охуевают, когда у каждого эндпоинта свой формат.
И глобальный guard — который на публичных эндпоинтах проверял не пользователя, а API-ключи. Типа «ты кто такой, давай, предъяви ключ, а то доверия к тебе — ноль ебать». Удобно, чёрт возьми. Всё на своих местах, как в хорошем механизме.