Для чего нужны guards в NestJS?

Ответ

Guards в NestJS — это механизм для авторизации и контроля доступа к маршрутам (эндпоинтам). Они выполняются до вызова обработчика запроса (controller method) и решают, следует ли разрешить выполнение запроса, основываясь на заданных условиях (например, наличие валидного JWT, проверка ролей пользователя).

Основная цель: Вынести логику проверки прав доступа из контроллеров в отдельные, переиспользуемые классы, соблюдая принцип единой ответственности (Single Responsibility Principle).

Пример реализации простого Guard для проверки аутентификации:

// auth.guard.ts
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);

    if (!token) {
      throw new UnauthorizedException('Токен отсутствует');
    }

    try {
      const payload = await this.jwtService.verifyAsync(token);
      // Присваиваем payload запросу, чтобы использовать в контроллере
      request['user'] = payload;
    } catch {
      throw new UnauthorizedException('Невалидный токен');
    }
    return true; // Запрос разрешен
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers['authorization']?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

Применение Guard: Его можно применить на уровне контроллера, метода или глобально.

// cats.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('cats')
@UseGuards(AuthGuard) // Весь контроллер защищен
// или @UseGuards(AuthGuard, RolesGuard) - несколько guards

export class CatsController {
  @Get('profile')
  getProfile(@Request() req) {
    // req.user доступен благодаря Guard
    return req.user;
  }
}

Ключевые преимущества использования Guards:

  • Чистота кода: Логика авторизации изолирована от бизнес-логики.
  • Переиспользование: Один Guard можно применить к множеству маршрутов.
  • Гибкость: Guards могут быть асинхронными, инжектировать сервисы (как JwtService в примере) и комбинироваться.
  • Интеграция с Exceptions: Легко интегрируются со встроенной системой исключений NestJS для возврата стандартизированных HTTP-ошибок (401, 403).

Ответ 18+ 🔞

А, Guards в NestJS! Ну это ж классика, ёпта. Представь себе такую ситуацию: у тебя есть бар, а на входе стоит здоровенный вышибала — вот это и есть Guard, только для твоих API-роутов. Его задача проста, как три копейки: проверить, можно ли пускать этого чувака внутрь, или послать его нахуй с невнятным криком «401 Unauthorized».

Зачем это, блядь, нужно? Ну смотри, без гвардов твоя бизнес-логика в контроллерах превращается в пиздопроебибну. Ты в каждом методе начинаешь с одного и того же: «а есть ли токен?», «а тот ли это пользователь?», «а не пидарас ли шерстяной?». Это же пиздец как неудобно! Гварды выносят всю эту муть в отдельный класс, чтобы твой контроллер занимался своим делом — отдавал котиков или что ты там делаешь — а не городил проверки.

Вот тебе простейший пример, как этот вышибала может выглядеть:

// auth.guard.ts — наш верный стражник
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);

    if (!token) {
      throw new UnauthorizedException('Токен отсутствует');
    }

    try {
      const payload = await this.jwtService.verifyAsync(token);
      // Кладём инфу о юзере в запрос, чтобы внутри контроллера к ней можно было доебаться
      request['user'] = payload;
    } catch {
      throw new UnauthorizedException('Невалидный токен');
    }
    return true; // Всё чики-пуки, проходи
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers['authorization']?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

А вот как этого стража натравить на свои эндпоинты:

// cats.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

@Controller('cats')
@UseGuards(AuthGuard) // Весь контроллер теперь под охраной. Хуй с горы не пролезет.
// Можно и несколько гвардов повесить, например, @UseGuards(AuthGuard, RolesGuard)

export class CatsController {
  @Get('profile')
  getProfile(@Request() req) {
    // req.user уже лежит тут, спасибо нашему гварду
    return req.user;
  }
}

Так в чём же, блядь, магия и охуенность?

  • Красота и порядок: В контроллере — чистая бизнес-логика, без всякого спагетти из проверок. Э бошка думай только о деле.
  • Переиспользование на овердохуища: Написал один раз — используй на тысяче роутов. Не надо копипастить одно и то же.
  • Мощь и гибкость: Они могут быть асинхронными, могут внедрять любые сервисы (как JwtService выше) и отлично дружат с системой исключений Nest. Хочешь — кинь 403, хочешь — 401.
  • Доверия ебать ноль: Они выполняются до того, как запрос доберётся до твоего обработчика. Так что если что-то не так — запрос даже не начнёт выполняться, сэкономив тебе ресурсы.

Короче, если не используешь гварды — ты, прости, распиздяй. Это базовый кирпич для любой нормальной авторизации, без него как без штанов.