Bordered avatar

Street Learner

Author
6 min read

Last Updated: a year ago

Clean and Reusable Code in NestJS: Custom Parameter Decorators with ExecutionContext

Clean and Reusable Code in NestJS: Custom Parameter Decorators with ExecutionContext

When building real-world NestJS applications, especially e-commerce platforms, controllers can get messy if you repeatedly extract data from requests. For example, getting the current logged-in user, client IP, or session info in every route can lead to repetitive code.

NestJS provides a powerful tool called Custom Parameter Decorators. Combined with ExecutionContext, they allow you to extract, transform, and reuse data across your controllers in a clean and type-safe way.

This blog shows how to create custom decorators, why they’re useful, and practical examples for an e-commerce backend.

Why Use Custom Parameter Decorators

  • Avoid repeating req.user or req.headers extraction
  • Make controllers cleaner and easier to maintain
  • Work seamlessly with middleware, guards, and interceptors
  • Support HTTP, WebSockets, and GraphQL routes
  • Enhance readability for teams and code reviewers

ExecutionContext in a Nutshell

ExecutionContext gives your decorator access to:

  • The request and response objects
  • The handler (controller method) and class
  • The context type (HTTP, GraphQL, WebSocket)

This allows you to build versatile decorators that work in multiple NestJS modules.

E-Commerce Scenario

Imagine an online store:

  • Every request contains a user token
  • Many controllers need current user information
  • Without decorators, controllers are cluttered:
@Get('cart')
async getCart(@Req() req: Request) {
  const user = req['user'];
  return this.cartService.getCart(user.id);
}

With a custom decorator, the same logic becomes:

@Get('cart')
async getCart(@CurrentUser() user: User) {
  return this.cartService.getCart(user.id);
}

Much cleaner and reusable.

Step 1: Create the Custom Decorator

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user; // assumes middleware attached user
  },
);
  • createParamDecorator creates the decorator
  • ExecutionContext gives access to the request
  • data allows optional fields, e.g., only getting id or role

Step 2: Attach User via Middleware

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const token = req.headers['authorization'];
    if (!token || token !== 'Bearer valid-shop-token') {
      throw new UnauthorizedException('Unauthorized');
    }

    req['user'] = { id: '9001', name: 'Hamza', role: 'customer' };
    next();
  }
}

Middleware ensures that req.user exists before controllers use @CurrentUser().

Step 3: Use the Decorator in Controllers

@Controller('orders')
export class OrdersController {
  constructor(private readonly ordersService: OrdersService) {}

  @Get()
  async getOrders(@CurrentUser() user: { id: string; role: string }) {
    console.log('User info from decorator:', user);
    return this.ordersService.getOrdersForUser(user.id);
  }
}
  • Cleaner code
  • Reusable across multiple controllers
  • Works automatically with guards or interceptors

Step 4: Optional Parameter in Decorator

You can pass a field to get specific user properties:

export const CurrentUser = createParamDecorator(
  (field: string, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;
    return field ? user?.[field] : user;
  },
);

Usage:

@Get()
async getOrders(@CurrentUser('id') userId: string) {
  return this.ordersService.getOrdersForUser(userId);
}

Step 5: Multi-Context Support

For GraphQL:

import { GqlExecutionContext } from '@nestjs/graphql';

const ctx = GqlExecutionContext.create(executionContext);
const user = ctx.getContext().req.user;

Your decorator now works with HTTP and GraphQL routes, which is extremely useful in real-world e-commerce apps.

Real-World E-Commerce Examples for Custom Decorators

  1. @CurrentUser() → attach current user info
  2. @ClientIp() → get client IP for fraud detection
  3. @RequestId() → unique request ID for logging
  4. @CurrentTenant() → multi-tenant apps
  5. @SessionData('cart') → get cart info from session

These decorators help you keep controllers simple and focused on business logic.

Senior Developer Tips

  • Always use decorators to avoid repeated extraction code
  • Combine with guards and middleware for authentication + authorization
  • Keep decorator logic lightweight to avoid performance hits
  • Decorators + ExecutionContext = reusable, clean, maintainable, type-safe code

Conclusion

Custom parameter decorators in NestJS allow developers to write clean, DRY, and reusable code. In e-commerce applications:

  • Middleware attaches user identity
  • Decorators extract user data effortlessly
  • Controllers remain focused on business logic
  • Guards or interceptors handle roles, policies, and caching

Learning to combine middleware, decorators, and ExecutionContext is a hallmark of senior-level NestJS engineering.

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