Bordered avatar

Street Learner

Author
6 min read

Last Updated: a year ago

Mastering Custom Guards & Policy-Based Authorization in NestJS

Mastering Custom Guards & Policy-Based Authorization in NestJS

Security is a critical part of any backend application. In NestJS, you can implement fine-grained access control using Custom Guards along with policy-based authorization, which includes RBAC (Role-Based Access Control) and ABAC (Attribute-Based Access Control).

Today, we will create a brand-new NestJS project and demonstrate how to implement robust authorization for an e-commerce app.

✅ What You Will Learn Today

  • What guards are and how they work in NestJS
  • How to implement RBAC (Role-Based Access Control)
  • How to implement ABAC (Attribute-Based Access Control)
  • Real-world examples for e-commerce
  • Creating a secure and maintainable authorization system

🚀 Step 1: Create a New NestJS Project

nest new nest-day7-guards-rbac-abac

Choose npm or yarn. Then generate products and orders modules:

nest generate module products
nest generate controller products
nest generate service products

nest generate module orders
nest generate controller orders
nest generate service orders

🔍 Step 2: Create Roles for RBAC

export enum UserRole {
  ADMIN = 'admin',
  CUSTOMER = 'customer',
  SELLER = 'seller',
}

🔥 Step 3: Create a Roles Decorator

import { SetMetadata } from '@nestjs/common';
import { UserRole } from '../roles.enum';

export const Roles = (...roles: UserRole[]) => SetMetadata('roles', roles);
  • Attaches roles metadata to controller methods
  • Useful for RBAC implementation

⚡ Step 4: Create Roles Guard (RBAC)

import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { UserRole } from '../roles.enum';

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

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.get<UserRole[]>('roles', context.getHandler());
    if (!requiredRoles) return true; // no roles required

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

    if (!user || !requiredRoles.includes(user.role)) {
      throw new ForbiddenException('Access Denied');
    }

    return true;
  }
}

⚡ Step 5: Apply RBAC Guard to Routes

import { Controller, Post, UseGuards } from '@nestjs/common';
import { Roles } from '../decorators/roles.decorator';
import { UserRole } from '../roles.enum';
import { RolesGuard } from '../guards/roles.guard';

@Controller('products')
export class ProductsController {
  @Post()
  @Roles(UserRole.ADMIN, UserRole.SELLER)
  @UseGuards(RolesGuard)
  createProduct() {
    return { message: 'Product created successfully' };
  }
}

✅ Only admin and seller roles can create products.

🔍 Step 6: Implement ABAC Guard (Ownership)

Sometimes you want resource-specific access, like letting a user update only their own orders.

import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { OrdersService } from '../orders/orders.service';

@Injectable()
export class OwnershipGuard implements CanActivate {
  constructor(private ordersService: OrdersService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();
    const user = req.user;
    const orderId = req.params.id;

    const order = await this.ordersService.findById(orderId);
    if (!order) throw new ForbiddenException('Order not found');

    return order.userId === user.id;
  }
}

⚡ Step 7: Apply ABAC Guard to Controller Route

import { Controller, Patch, Param, UseGuards } from '@nestjs/common';
import { OwnershipGuard } from '../guards/ownership.guard';

@Controller('orders')
export class OrdersController {
  @Patch(':id')
  @UseGuards(OwnershipGuard)
  updateOrder(@Param('id') id: string) {
    return { message: `Order ${id} updated` };
  }
}

✅ Only the owner of the order can update it.

🛒 Step 8: Real-World E-Commerce Use Cases

  • RBAC: Admin panel access Seller dashboard Discount code management
  • Admin panel access
  • Seller dashboard
  • Discount code management
  • ABAC: Update or cancel your own orders only Modify products in your assigned store (multi-tenant) Apply special promotions for specific customer tiers
  • Update or cancel your own orders only
  • Modify products in your assigned store (multi-tenant)
  • Apply special promotions for specific customer tiers

🧠 Senior Developer Tips

  1. RBAC for roles — broad access control
  2. ABAC for resource ownership — fine-grained control
  3. Never put authorization logic in controllers — use guards
  4. Combine RBAC + ABAC for maximum security
  5. Use custom decorators to simplify extracting user info

✅ Step 9: Bonus — Combine with Decorators

@Patch('orders/:id')
@UseGuards(OwnershipGuard)
updateOrder(@Param('id') id: string, @CurrentUser() user) {
  return { message: `User ${user.id} updated order ${id}` };
}
  • Keeps controller clean and readable
  • Guards handle all access control

🏁 Conclusion

Custom Guards with RBAC and ABAC in NestJS provide:

  • Secure endpoints
  • Flexible, fine-grained access control
  • Reusable authorization logic
  • Maintainable and scalable code

For e-commerce applications, this is critical for admin panels, seller dashboards, and user order management.

Related Stories



Performance Optimization of NestJS Applications: A Complete Guide

Performance Optimization of NestJS Applications: A Complete Guide

Optimizing the performance of a NestJS application is critical for building scalable, fast, and production-ready APIs. Even though NestJS is a high-performance framework, improper coding practices, unoptimized database queries, and lack of caching can slow down your application.

Bordered avatar

Street Learner

21 Jan 2025

Mastering High-Performance Interceptors in NestJS for Scalable APIs

Mastering High-Performance Interceptors in NestJS for Scalable APIs

NestJS interceptors are one of the most powerful tools in the framework, enabling developers to transform responses, cache results, log performance, and optimize requests. For large-scale applications, building high-performance interceptors is essential to improve speed, maintainability, and scalability.

Bordered avatar

Street Learner

18 Jan 2025