Guards

Intermedio
Nest
Nest
Actualizado: 05/05/2026

Diagrama: tutorial-nest-guards

Introducción a los Guards

Los Guards en NestJS son una característica fundamental del sistema de seguridad que permite controlar el acceso a rutas y recursos específicos de tu aplicación. Funcionan como filtros de autorización que se ejecutan antes de que una petición llegue al controlador, determinando si debe procesarse o rechazarse.

Un Guard implementa la interfaz CanActivate y debe devolver un valor booleano que indica si la petición actual está autorizada para continuar. Esta decisión puede basarse en diversos factores como la autenticación del usuario, sus permisos, roles o cualquier lógica de negocio específica.

Funcionamiento básico de los Guards

Los Guards se ejecutan después de los middlewares pero antes de los interceptors y pipes. Esta posición en el ciclo de vida de la petición los convierte en el lugar ideal para implementar lógica de autorización, ya que pueden detener el procesamiento antes de que se ejecute cualquier lógica de negocio.

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    
    // Lógica de validación
    return this.validateRequest(request);
  }

  private validateRequest(request: any): boolean {
    // Ejemplo: verificar si existe un token de autorización
    return request.headers.authorization !== undefined;
  }
}

Tipos de Guards en NestJS

NestJS permite implementar diferentes tipos de Guards según las necesidades de tu aplicación:

Guards de autenticación: Verifican si el usuario está autenticado correctamente. Suelen validar tokens JWT, cookies de sesión o credenciales básicas.

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

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    
    if (!token) {
      return false;
    }

    try {
      const payload = this.jwtService.verify(token);
      request.user = payload;
      return true;
    } catch {
      return false;
    }
  }

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

Guards de autorización: Verifican si el usuario autenticado tiene los permisos necesarios para acceder a un recurso específico. Evalúan roles, permisos o políticas de acceso.

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!requiredRoles) {
      return true;
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;

    return requiredRoles.some((role) => user.roles?.includes(role));
  }
}

Aplicación de Guards

Los Guards pueden aplicarse a diferentes niveles de tu aplicación, proporcionando flexibilidad en el control de acceso:

A nivel global: Afecta a todas las rutas de la aplicación.

import { APP_GUARD } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
})
export class AppModule {}

A nivel de controlador: Protege todas las rutas de un controlador específico.

@Controller('users')
@UseGuards(AuthGuard)
export class UsersController {
  @Get()
  findAll() {
    return 'Esta ruta está protegida por el AuthGuard';
  }
}

A nivel de método: Protege únicamente una ruta específica.

@Controller('users')
export class UsersController {
  @Get('profile')
  @UseGuards(AuthGuard, RolesGuard)
  getProfile() {
    return 'Ruta protegida por múltiples guards';
  }
}

ExecutionContext y acceso a datos

El ExecutionContext proporciona acceso completo a los detalles de la petición actual. Este contexto permite a los Guards examinar la petición HTTP, extraer headers, parámetros y cualquier información necesaria para tomar decisiones de autorización.

@Injectable()
export class OwnershipGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    const resourceId = request.params.id;

    // Verificar si el usuario es propietario del recurso
    return this.checkOwnership(user.id, resourceId);
  }

  private checkOwnership(userId: string, resourceId: string): boolean {
    // Lógica para verificar propiedad del recurso
    return true; // Simplificado para el ejemplo
  }
}

Los Guards representan una herramienta esencial para implementar seguridad robusta en aplicaciones NestJS. Su integración natural con el sistema de inyección de dependencias permite crear soluciones de autorización complejas y reutilizables que se adaptan perfectamente a los requisitos de seguridad de aplicaciones empresariales.

Decoradores @Public y @Roles con Reflector

En proyectos enterprise se aplica un guard JWT global y se exime mediante un decorador @Public():

import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(
    private readonly jwtService: JwtService,
    private readonly reflector: Reflector,
  ) {}

  canActivate(context: ExecutionContext): boolean {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) return true;

    const request = context.switchToHttp().getRequest();
    const token = request.headers.authorization?.split(' ')[1];
    if (!token) throw new UnauthorizedException('Token ausente');
    try {
      request.user = this.jwtService.verify(token);
      return true;
    } catch {
      throw new UnauthorizedException('Token inválido');
    }
  }
}

@Controller('auth')
export class AuthController {
  @Public()
  @Post('login')
  login(@Body() dto: LoginDto) { /* ... */ }
}

Patrón secure-by-default: la API exige token salvo que el endpoint declare explícitamente que es público. Un olvido (no añadir @Public()) bloquea el endpoint, que es el comportamiento seguro en producción.

Guards asíncronos y autorización por dominio

canActivate puede devolver Promise<boolean> para consultar base de datos o un servicio externo. Caso típico: un usuario solo puede operar sobre recursos cuyo tenantId coincide:

@Injectable()
export class TenantGuard implements CanActivate {
  constructor(private readonly resourceService: ResourceService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();
    const resourceId = req.params.id;
    const userTenant = req.user.tenantId;
    const resource = await this.resourceService.findById(resourceId);
    if (!resource) throw new NotFoundException(`Recurso ${resourceId} no existe`);
    if (resource.tenantId !== userTenant) {
      throw new ForbiddenException('Recurso no pertenece a tu organización');
    }
    return true;
  }
}

Combinación con interceptors y exception filters

Cuando el guard rechaza, NestJS lanza ForbiddenException por defecto. Un ExceptionFilter global puede normalizar la respuesta a formato RFC 7807 (Problem Details):

@Catch(HttpException)
export class ProblemDetailsFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const status = exception.getStatus();
    response.status(status).json({
      type: `https://api.example.com/errors/${exception.name}`,
      title: exception.message,
      status,
      detail: exception.getResponse(),
      instance: ctx.getRequest().url,
    });
  }
}

Tests de guards

Los guards se prueban aislados con un ExecutionContext mockeado:

import { createMock } from '@golevelup/ts-jest';

const guard = new RolesGuard(new Reflector());
const ctx = createMock<ExecutionContext>({
  switchToHttp: () => ({ getRequest: () => ({ user: { role: 'user' } }) }),
  getHandler: () => function () {},
  getClass: () => class {},
});
jest.spyOn(Reflector.prototype, 'getAllAndOverride').mockReturnValue(['admin']);
expect(guard.canActivate(ctx)).toBe(false);

Probar guards aislados permite cubrir casos límite (token expirado, rol incorrecto, recurso de otro tenant) sin levantar el módulo entero.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Nest

Documentación oficial de Nest
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Nest es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Nest

Explora más contenido relacionado con Nest y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Comprender qué son los Guards y su función en el ciclo de vida de una petición en NestJS. Aprender a implementar Guards personalizados que controlen la autenticación y autorización. Conocer los diferentes tipos de Guards y cuándo aplicarlos (autenticación, roles, propiedad). Saber cómo aplicar Guards a nivel global, de controlador y de método. Entender el uso de ExecutionContext para acceder a la información de la petición y tomar decisiones de seguridad.