
Guards
- 2 installs
- 1 repo stars
- Updated January 2, 2026
- ramziddin/ccplugins
Reference for NestJS guards: authentication, authorization, route protection, JWT validation, and RBAC via the CanActivate interface.
About
Covers implementing guards in NestJS to allow or deny requests based on identity, roles, or request context using CanActivate. A developer uses it to protect routes with auth, RBAC, or conditional access.
- Implement CanActivate to allow or deny a request
- Run after middleware but before interceptors and pipes
Guards by the numbers
- 2 all-time installs (skills.sh)
- Ranked #3,774 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/ramziddin/ccplugins --skill guardsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 1 |
| Last updated | January 2, 2026 |
| Repository | ramziddin/ccplugins ↗ |
What it does
Reference for NestJS guards: authentication, authorization, route protection, JWT validation, and RBAC via the CanActivate interface.
Files
NestJS Guards
When to Use This Skill
Use this skill when:
- Implementing authentication (verifying user identity)
- Implementing authorization (checking user permissions/roles)
- Protecting routes based on user roles or permissions
- Validating JWT tokens or API keys
- Implementing custom access control logic
- Restricting access based on request context
- Building role-based access control (RBAC) systems
- Implementing feature flags or conditional route access
What are Guards?
Guards determine whether a request will be handled by the route handler. They implement the CanActivate interface and return a boolean value (or Promise/Observable) indicating whether access is allowed.
Guards execute after middleware but before interceptors and pipes in the request lifecycle.
Basic Guard
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();
return validateRequest(request);
}
}
function validateRequest(request: any): boolean {
return true; // Replace with actual validation logic
}Key Points:
- Decorated with
@Injectable() - Implements
CanActivateinterface canActivate()returns boolean (true = allow, false = deny)- Has access to
ExecutionContextfor detailed request information - Throws
ForbiddenExceptionwhen returning false
Execution Context
ExecutionContext extends ArgumentsHost and provides additional context about the current execution:
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// Get HTTP-specific context
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
// Get handler and class references
const handler = context.getHandler();
const controller = context.getClass();
return true;
}
}Binding Guards
Method-scoped Guard
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('cats')
export class CatsController {
@Get()
@UseGuards(AuthGuard)
findAll() {
return 'This route is protected';
}
}Controller-scoped Guard
import { Controller, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {
// All routes in this controller are protected
@Get()
findAll() {
return 'Protected route';
}
}Global Guard
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AppModule {}Alternative (in main.ts):
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());Role-based Authorization Guard
Setting Roles with Custom Decorator
// roles.decorator.ts
import { SetMetadata } from '@nestjs/core';
export enum Role {
User = 'user',
Admin = 'admin',
Moderator = 'moderator',
}
export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);Using Roles Decorator
import { Controller, Get } from '@nestjs/common';
import { Roles, Role } from './roles.decorator';
@Controller('cats')
export class CatsController {
@Get()
@Roles(Role.Admin)
findAll() {
return 'Only admins can access this';
}
}Roles Guard Implementation
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY, Role } from './roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true; // No roles required, allow access
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}JWT Authentication Guard
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
@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 {
const payload = await this.jwtService.verifyAsync(token, {
secret: process.env.JWT_SECRET,
});
request['user'] = payload;
} catch {
throw new UnauthorizedException('Invalid token');
}
return true;
}
private extractTokenFromHeader(request: Request): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}API Key Guard
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class ApiKeyGuard implements CanActivate {
constructor(private configService: ConfigService) {}
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const apiKey = request.headers['x-api-key'];
const validApiKey = this.configService.get('API_KEY');
if (!apiKey || apiKey !== validApiKey) {
throw new UnauthorizedException('Invalid API key');
}
return true;
}
}Combining Multiple Guards
@Controller('cats')
@UseGuards(AuthGuard, RolesGuard)
export class CatsController {
@Get()
@Roles(Role.Admin)
findAll() {
return 'Protected by both guards';
}
}Guards execute in the order they are applied (left to right).
Public Routes (Skipping Guards)
Public Decorator
// public.decorator.ts
import { SetMetadata } from '@nestjs/core';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);Modified Auth Guard
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from './public.decorator';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
// Perform authentication check
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}Using Public Decorator
@Controller('auth')
export class AuthController {
@Public()
@Post('login')
login() {
return 'This route is public';
}
}Permission-based Guard
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
export enum Permission {
CreateCat = 'create:cat',
ReadCat = 'read:cat',
UpdateCat = 'update:cat',
DeleteCat = 'delete:cat',
}
export const PERMISSIONS_KEY = 'permissions';
export const RequirePermissions = (...permissions: Permission[]) =>
SetMetadata(PERMISSIONS_KEY, permissions);
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredPermissions = this.reflector.getAllAndOverride<Permission[]>(
PERMISSIONS_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredPermissions) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredPermissions.every((permission) =>
user.permissions?.includes(permission),
);
}
}
// Usage
@Controller('cats')
export class CatsController {
@Get()
@RequirePermissions(Permission.ReadCat)
findAll() {
return 'Requires read permission';
}
@Delete(':id')
@RequirePermissions(Permission.DeleteCat)
remove() {
return 'Requires delete permission';
}
}Reflector Service
The Reflector class is used to retrieve metadata set by decorators:
constructor(private reflector: Reflector) {}
// Get metadata from handler only
const roles = this.reflector.get<string[]>('roles', context.getHandler());
// Get metadata from class only
const roles = this.reflector.get<string[]>('roles', context.getClass());
// Get metadata from both (handler overrides class)
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
// Get metadata from both (merge arrays)
const roles = this.reflector.getAllAndMerge<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);Feature Flag Guard
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ConfigService } from '@nestjs/config';
export const FEATURE_FLAG_KEY = 'featureFlag';
export const RequireFeature = (flag: string) =>
SetMetadata(FEATURE_FLAG_KEY, flag);
@Injectable()
export class FeatureFlagGuard implements CanActivate {
constructor(
private reflector: Reflector,
private configService: ConfigService,
) {}
canActivate(context: ExecutionContext): boolean {
const requiredFeature = this.reflector.get<string>(
FEATURE_FLAG_KEY,
context.getHandler(),
);
if (!requiredFeature) {
return true;
}
const isEnabled = this.configService.get(`features.${requiredFeature}`);
return isEnabled === true;
}
}
// Usage
@Controller('beta')
export class BetaController {
@Get('feature')
@RequireFeature('newFeature')
getBetaFeature() {
return 'Beta feature';
}
}Complete Example
// auth.guard.ts
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { IS_PUBLIC_KEY } from './decorators/public.decorator';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private jwtService: JwtService,
private reflector: Reflector,
) {}
async canActivate(context: ExecutionContext): Promise<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 = 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: any): string | undefined {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : undefined;
}
}
// roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from './decorators/roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
ROLES_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) => user.roles?.includes(role));
}
}
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './guards/auth.guard';
import { RolesGuard } from './guards/roles.guard';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
})
export class AppModule {}
// Usage in controller
@Controller('cats')
export class CatsController {
@Get()
@Roles(Role.Admin)
findAll() {
return 'Admin only';
}
@Post('login')
@Public()
login() {
return 'Public route';
}
}Best Practices
1. Single responsibility - Each guard should check one thing 2. Use dependency injection - Inject services for complex validation 3. Throw appropriate exceptions - Use UnauthorizedException or ForbiddenException 4. Leverage Reflector - Use custom decorators for metadata 5. Order matters - Apply guards in the correct order (auth before roles) 6. Make routes public explicitly - Use @Public() decorator for clarity 7. Return early - Check for public routes first to avoid unnecessary processing 8. Use global guards - For application-wide authentication 9. Cache metadata - Use getAllAndOverride for better performance 10. Document guard behavior - Clear comments on what each guard protects
Guards vs Middleware
Guards:
- Execute after middleware
- Have access to
ExecutionContext - Know which handler will execute
- Better for authentication/authorization
- Can use dependency injection easily
- Work across all transport layers (HTTP, WebSockets, Microservices)
Middleware:
- Execute before guards
- No access to execution context
- Don't know which handler will execute
- Better for general request processing
- Simpler for basic operations
Request Lifecycle Position
Incoming Request
↓
Middleware
↓
Guards ← (You are here)
↓
Interceptors (before)
↓
Pipes
↓
Route Handler
↓
Interceptors (after)
↓
Exception Filters
↓
ResponseGuards are the gatekeepers that decide if a request should proceed to the handler based on the execution context.