title: 'Guards - NL Framework' description: 'Learn how to use guards for authorization and access control in NL Framework'

Overview

Guards

Guards are a powerful mechanism for implementing authorization logic in your application. They determine whether a request should be handled by the route handler or not, making them perfect for authentication, authorization, and access control scenarios.

💡 Key Concept: Guards execute after all middleware but before pipes and route handlers. They have access to the full execution context and can make complex authorization decisions.

Guard Interface

Guards implement the CanActivate interface, which defines a single method:

Basic AuthGuard implementation
import { CanActivate, HttpGuardExecutionContext } from '@nl-framework/http';export class AuthGuard implements CanActivate {  async canActivate(context: HttpGuardExecutionContext): Promise<boolean> {    const request = context.getRequest();    const token = request.headers.authorization;        if (!token) {      return false;    }        try {      const user = await this.validateToken(token);      request.user = user;      return true;    } catch (error) {      return false;    }  }    private async validateToken(token: string) {    return { id: 1, username: 'john' };  }}

Using Guards

Guards can be applied to controllers or individual route handlers using the @UseGuards() decorator:

Method-Level Guards

Apply guards to specific routes
import { Controller, Get, UseGuards } from '@nl-framework/http';@Controller('/api/users')export class UserController {  @Get('/')  @UseGuards(AuthGuard)  async getUsers() {    return { users: [] };  }  @Get('/public')  async getPublicData() {    return { data: 'public' };  }}

Controller-Level Guards

Protect all routes in a controller
@Controller('/api/admin')@UseGuards(AuthGuard, AdminGuard)export class AdminController {  @Get('/users')  async getUsers() {    return { users: [] };  }    @Delete('/users/:id')  async deleteUser(id: string) {    return { deleted: true };  }}

Functional Guards

For simple authorization logic, you can use functional guards instead of classes:

Functional guard examples
import { GuardFunction } from '@nl-framework/http';const isAuthenticated: GuardFunction = async (context) => {  const request = context.getRequest();  return !!request.headers.authorization;};function hasRole(role: string): GuardFunction {  return async (context) => {    const request = context.getRequest();    return request.user?.role === role;  };}@Controller('/api/admin')export class AdminController {  @Get('/dashboard')  @UseGuards(isAuthenticated, hasRole('admin'))  async getDashboard() {    return { dashboard: {} };  }}

Global Guards

Register guards globally to apply them to all routes:

Register global guards
import { createHttpApp, registerHttpGuard } from '@nl-framework/http';async function bootstrap() {  const app = await createHttpApp({ port: 3000 });  registerHttpGuard(AuthGuard);  await app.start();}

Advanced Patterns

Role-Based Access Control (RBAC)

RBAC with custom decorators
import { SetMetadata } from '@nl-framework/core';export const Roles = (...roles: string[]) => SetMetadata('roles', roles);@Injectable()export class RolesGuard implements CanActivate {  async canActivate(context: HttpGuardExecutionContext): Promise<boolean> {    const requiredRoles = context.getRoute().metadata?.roles as string[];    if (!requiredRoles) return true;    const user = context.getRequest().user;    return requiredRoles.some(role => user.roles?.includes(role));  }}@Controller('/api/admin')@UseGuards(AuthGuard, RolesGuard)export class AdminController {  @Get('/users')  @Roles('admin', 'superadmin')  async getUsers() {    return { users: [] };  }}

Best Practices

Keep Guards Focused: Each guard should have a single responsibility.
Use Dependency Injection: Leverage DI to inject services and repositories.
Attach User to Request: After authentication, attach the user object to the request.
Order Matters: Apply guards in logical order: authentication → authorization → resource checks.
Provide Clear Error Messages: Help clients understand why access was denied.

✅ Summary: Guards provide a clean, declarative way to implement authorization logic. They execute after middleware but before pipes and handlers, support both class-based and functional implementations, and can be applied globally, per-controller, or per-route.