← Назад к вопросам
Что такое Guards в NestJS?
2.3 Middle🔥 132 комментариев
#Безопасность#Фреймворки и библиотеки
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Guards в NestJS: что это, как использовать, примеры
Guards (охранники) в NestJS — это классы, которые определяют, будет ли обработан тот или иной запрос. Это мощный инструмент для контроля доступа и авторизации.
Основное определение
Guard — это класс, который реализует интерфейс CanActivate и содержит метод canActivate(), который возвращает boolean:
true— разрешить обработку запросаfalse— отклонить запрос
Простейший Guard
// guards/simple.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
@Injectable()
export class SimpleGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
return true; // Всегда разрешить
}
}
// Использование на контроллере
import { UseGuards } from '@nestjs/common';
@UseGuards(SimpleGuard)
@Controller('users')
export class UserController {}
Guard для JWT авторизации (самый частый случай)
// guards/jwt-auth.guard.ts
import {
Injectable,
UnauthorizedException,
CanActivate,
ExecutionContext
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class JwtAuthGuard 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('No token provided');
}
try {
// Верифицируем JWT токен
const payload = await this.jwtService.verifyAsync(token, {
secret: process.env.JWT_SECRET
});
// Сохраняем payload в request для использования в контроллере
request.user = payload;
return true;
} catch (error) {
throw new UnauthorizedException('Invalid token');
}
}
private extractTokenFromHeader(request: any): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
// Использование
@UseGuards(JwtAuthGuard)
@Get('profile')
async getProfile(@Request() req) {
return req.user; // Получаем payload из guard'а
}
Guard для проверки ролей (Role-based Access Control)
// guards/roles.guard.ts
import {
Injectable,
CanActivate,
ExecutionContext,
ForbiddenException
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// Получаем требуемые роли из метаданных (установлены декоратором)
const requiredRoles = this.reflector.get<string[]>(
'roles',
context.getHandler()
);
// Если роли не установлены - разрешить доступ
if (!requiredRoles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
if (!user) {
throw new ForbiddenException('User not found');
}
// Проверяем, есть ли у пользователя одна из требуемых ролей
const hasRole = requiredRoles.some((role) => user.roles?.includes(role));
if (!hasRole) {
throw new ForbiddenException(
`This action requires one of these roles: ${requiredRoles.join(', ')}`
);
}
return true;
}
}
// Создаём кастомный декоратор для установки ролей
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
// Использование
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin', 'moderator')
@Delete(':id')
async deleteUser(@Param('id') id: string) {
return this.userService.delete(id);
}
Guard для проверки владельца ресурса
// guards/owner.guard.ts
import {
Injectable,
CanActivate,
ExecutionContext,
ForbiddenException,
NotFoundException
} from '@nestjs/common';
import { UserService } from '../services/user.service';
@Injectable()
export class OwnerGuard implements CanActivate {
constructor(private userService: UserService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const { id } = request.params;
const currentUser = request.user;
// Получаем ресурс из БД
const resource = await this.userService.getById(id);
if (!resource) {
throw new NotFoundException('Resource not found');
}
// Проверяем, является ли текущий пользователь владельцем
if (resource.userId !== currentUser.id && currentUser.role !== 'admin') {
throw new ForbiddenException('You can only modify your own resource');
}
return true;
}
}
// Использование
@UseGuards(JwtAuthGuard, OwnerGuard)
@Put(':id')
async updateProfile(@Param('id') id: string, @Body() data: UpdateUserDTO) {
return this.userService.update(id, data);
}
Guard с асинхронной логикой
// guards/subscription.guard.ts - проверка наличия подписки
import {
Injectable,
CanActivate,
ExecutionContext,
PaymentRequiredException
} from '@nestjs/common';
import { SubscriptionService } from '../services/subscription.service';
@Injectable()
export class SubscriptionGuard implements CanActivate {
constructor(private subscriptionService: SubscriptionService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const user = request.user;
// Асинхронно проверяем подписку в БД
const subscription = await this.subscriptionService.getUserSubscription(
user.id
);
if (!subscription || subscription.status !== 'active') {
throw new PaymentRequiredException(
'You need an active subscription to access this resource'
);
}
// Можем также сохранить данные подписки в request
request.subscription = subscription;
return true;
}
}
// Использование
@UseGuards(JwtAuthGuard, SubscriptionGuard)
@Get('premium-feature')
async getPremiumData(@Request() req) {
return {
data: 'premium',
subscription: req.subscription
};
}
Комбинирование нескольких Guard'ов
// Все эти guard'ы должны пройти проверку
@UseGuards(JwtAuthGuard, RolesGuard, SubscriptionGuard)
@Roles('admin')
@Post('admin/users')
async createUser(@Body() data: CreateUserDTO) {
// Здесь пользователь:
// 1. Аутентифицирован (JwtAuthGuard)
// 2. Имеет роль 'admin' (RolesGuard)
// 3. Имеет активную подписку (SubscriptionGuard)
return this.userService.create(data);
}
Guard с кэшированием результатов
// guards/cached-authorization.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { CacheService } from '../services/cache.service';
@Injectable()
export class CachedAuthorizationGuard implements CanActivate {
private cache = new Map<string, boolean>();
constructor(private cacheService: CacheService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const userId = request.user?.id;
const resource = request.params.id;
const cacheKey = `${userId}-${resource}`;
// Проверяем локальный кэш
const cachedResult = this.cache.get(cacheKey);
if (cachedResult !== undefined) {
return cachedResult;
}
// Проверяем Redis кэш
const redisResult = await this.cacheService.get(cacheKey);
if (redisResult !== null) {
return redisResult;
}
// Выполняем проверку доступа
const hasAccess = await this.checkAccess(userId, resource);
// Кэшируем результат на час
this.cache.set(cacheKey, hasAccess);
await this.cacheService.set(cacheKey, hasAccess, 3600);
return hasAccess;
}
private async checkAccess(userId: string, resource: string): Promise<boolean> {
// Реальная проверка доступа
return true;
}
}
Guard vs Middleware vs Interceptor
// Guard - проверка доступа
// Когда: перед обработкой контроллера
// Что делает: решает, обрабатывать ли запрос
// Пример: JWT авторизация
// Middleware - предварительная обработка
// Когда: перед guard'ами и контроллером
// Что делает: трансформирует request (добавляет данные)
// Пример: парсинг body
// Interceptor - перехват response'а
// Когда: после контроллера, перед отправкой ответа
// Что делает: трансформирует response, логирует
// Пример: добавление metadata к ответу
// Порядок выполнения:
Middleware -> Guard -> Interceptor (в контроллере) -> Guard -> Interceptor (ответ)
Лучшие практики для Guard'ов
-
Каждый Guard — одна ответственность
// ✅ Хорошо @UseGuards(JwtAuthGuard, RolesGuard, SubscriptionGuard) // ❌ Плохо - один большой Guard @UseGuards(MonsterGuard) // делает всё -
Используй Reflector для метаданных
const roles = this.reflector.get<string[]>('roles', context.getHandler()); -
Кэшируй результаты, если возможно
// Проверка прав может быть дорогой операцией // Кэшируй на несколько минут -
Выбросй правильные исключения
throw new UnauthorizedException(); // 401 - не аутентифицирован throw new ForbiddenException(); // 403 - нет доступа -
Тестируй Guard'ы
const executionContext = { switchToHttp: () => ({ getRequest: () => mockRequest }) }; const result = await guard.canActivate(executionContext);
Выводы
Guards в NestJS — это мощный и удобный способ реализовать:
- Авторизацию (JWT, OAuth, etc.)
- Role-based access control (RBAC)
- Проверку владения ресурсом
- Проверку подписок
- Кэширование результатов доступа
Правильное использование Guard'ов делает код более чистым и поддерживаемым.