Guards
Guards implement authorization logic that runs before endpoint handlers. They control access to your API endpoints.
What are Guards?
Guards are classes that implement the CanActivate interface. They receive the execution context and return a boolean indicating whether the request should proceed.
Key characteristics:
- Run before the endpoint handler executes
- Can access request data (headers, params, body)
- Return
trueto allow access,falseto deny - Can throw exceptions for specific error responses
- Can attach data to the request for use in handlers
Creating a Guard
Implement the CanActivate interface:
import { Injectable, CanActivate, AbstractExecutionContext } from '@navios/core'
@Injectable()
class AuthGuard implements CanActivate {
async canActivate(context: AbstractExecutionContext): Promise<boolean> {
const request = context.getRequest()
const token = request.headers.authorization
if (!token) {
return false // Returns 403 Forbidden
}
return true
}
}
Applying Guards
Module-Level Guards
Apply guards to all endpoints in a module:
@Module({
controllers: [UserController, ProfileController],
guards: [AuthGuard],
})
class UserModule {}
Endpoint-Level Guards
Apply guards to specific endpoints using @UseGuards():
import { UseGuards } from '@navios/core'
@Controller()
class UserController {
@Endpoint(getUser)
async getUser(params: EndpointParams<typeof getUser>) {
// Public endpoint - no guard
}
@Endpoint(deleteUser)
@UseGuards(AdminGuard)
async deleteUser(params: EndpointParams<typeof deleteUser>) {
// Protected by AdminGuard
}
}
Multiple Guards
Apply multiple guards - all must pass:
@Module({
controllers: [AdminController],
guards: [AuthGuard, AdminGuard],
})
class AdminModule {}
// Or on endpoint
@Endpoint(sensitiveAction)
@UseGuards(AuthGuard, AdminGuard)
async sensitiveAction() {}
Guard Execution Order
- Module guards run first (in array order)
- Endpoint guards run second (in array order)
- If any guard returns
falseor throws, the request is rejected
Accessing Request Data
Use the execution context to access request information:
@Injectable()
class AuthGuard implements CanActivate {
async canActivate(context: AbstractExecutionContext): Promise<boolean> {
const request = context.getRequest()
// Access headers
const authHeader = request.headers.authorization
// Access URL parameters
const userId = request.params.userId
// Access query parameters
const token = request.query.token
// Access body
const body = request.body
return true
}
}
Throwing Exceptions
Throw HTTP exceptions for specific error responses:
import { UnauthorizedException, ForbiddenException } from '@navios/core'
@Injectable()
class AuthGuard implements CanActivate {
async canActivate(context: AbstractExecutionContext): Promise<boolean> {
const request = context.getRequest()
const authHeader = request.headers.authorization
if (!authHeader) {
throw new UnauthorizedException('Missing authorization header')
}
if (!authHeader.startsWith('Bearer ')) {
throw new UnauthorizedException('Invalid authorization format')
}
return true
}
}
Attaching Data to Request
Guards often attach data for use in handlers:
@Injectable()
class AuthGuard implements CanActivate {
private userService = inject(UserService)
async canActivate(context: AbstractExecutionContext): Promise<boolean> {
const request = context.getRequest()
const userId = this.extractUserId(request)
if (!userId) {
throw new UnauthorizedException()
}
const user = await this.userService.findById(userId)
if (!user) {
throw new UnauthorizedException('User not found')
}
// Attach user to request
request.user = user
return true
}
}
Access in controller:
@Controller()
class ProfileController {
@Endpoint(getProfile)
async getProfile(params: EndpointParams<typeof getProfile>) {
const request = inject(Request)
return request.user // User attached by guard
}
}
Common Guard Patterns
Simple Token Check
@Injectable()
class ApiKeyGuard implements CanActivate {
private config = inject(ConfigService)
async canActivate(context: AbstractExecutionContext): Promise<boolean> {
const request = context.getRequest()
const apiKey = request.headers['x-api-key']
const validKey = this.config.getOrThrow('API_KEY')
if (apiKey !== validKey) {
throw new UnauthorizedException('Invalid API key')
}
return true
}
}
Role Check
@Injectable()
class AdminGuard implements CanActivate {
async canActivate(context: AbstractExecutionContext): Promise<boolean> {
const request = context.getRequest()
const user = request.user // Set by AuthGuard
if (!user || user.role !== 'admin') {
throw new ForbiddenException('Admin access required')
}
return true
}
}
Next Steps
- Authentication Guide - Comprehensive guide covering JWT, OAuth, Passport, sessions, and more
- JWT Authentication Recipe - Complete JWT implementation example