Пошук уроків, статей та іншого контенту
Як NestJS вирішує, чи дозволити запиту дістатись обробника — до Pipes і до самого контролера.
Guard відповідає на одне бінарне питання — чи дозволено цьому запиту дістатись обробника — і виконується раніше за Pipes, тобто до будь-якої валідації чи трансформації вхідних даних. Найпоширеніше застосування — перевірка автентифікації (чи є в запиті валідний токен) та авторизації (чи має цей користувач право виконати саме цю дію).
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common";
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization;
if (!token || !isValidToken(token)) {
throw new UnauthorizedException("Потрібна автентифікація");
}
return true; // запит допущено далі
}
}Механіка перевірки токена — та сама ідея, що й у статті «JWT Authentication: як це працює»: Guard перевіряє підпис і термін дії токена перед тим, як дозволити запиту продовжитись.
Декоратор @UseGuards() застосовує Guard до конкретного методу, цілого контролера (усі його маршрути) чи глобально до всього застосунку:
@Controller("cats")
@UseGuards(AuthGuard) // усі маршрути цього контролера вимагають автентифікації
export class CatsController {
@Get()
findAll() {
return this.catsService.findAll();
}
}Guard так само підходить для перевірок авторизації складніших за просту наявність токена — наприклад, чи має роль користувача право на конкретну дію, читаючи метадані, встановлені окремим декоратором на методі:
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRole = this.reflector.get<string>("role", context.getHandler());
const { user } = context.switchToHttp().getRequest();
return !requiredRole || user.role === requiredRole;
}
}Дублювати перевірку автентифікації вручну на початку кожного методу контролера замість одного Guard, підключеного декларативно — легко забути перевірку в новому методі.
Плутати Guard із Pipe за призначенням — Guard вирішує «чи можна взагалі обробляти цей запит» (виконується раніше), Pipe — «чи коректні й у правильному форматі дані цього запиту» (виконується пізніше).
Повертати false із canActivate замість кидати конкретний виняток (UnauthorizedException, ForbiddenException) там, де клієнту потрібне зрозуміле повідомлення про причину відмови, а не просто загальний код 403.
Guard визначає, чи допустити запит далі, — типово для перевірки автентифікації й авторизації, — і виконується раніше за Pipes, ще до валідації самих даних запиту. Підключається декларативно через @UseGuards() на метод, контролер чи весь застосунок, що усуває дублювання ручних перевірок на початку кожного обробника.