Decorators

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

Decorators are a core language feature of TypeScript and the primary mechanism NestJS uses to attach metadata to classes, methods, and properties.

Overview

A Decorator is a special kind of declaration that can be attached to a class declaration, method, accessor, property, or parameter. Decorators use the form @expression, where expression must evaluate to a function that will be called at runtime with information about the decorated declaration.

NestJS uses decorators extensively to configure routing, define dependencies, set up middleware, and configure metadata for the underlying Express/Fastify server.

Key Concepts

  • Class Decorators: Used to define Modules (@Module()), Controllers (@Controller()), and Injectables (@Injectable()).
  • Method Decorators: Used to define HTTP route handlers (@Get(), @Post()) and map execution logic (@UseGuards(), @UseInterceptors()).
  • Parameter Decorators: Used to extract data from the incoming request (@Body(), @Query(), @Param()).
  • Custom Decorators: You can create your own custom decorators to extract specific data from a request in a clean, reusable way.

Code Examples

Standard NestJS Decorators

import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';

// Class decorator
@Controller('users')
export class UsersController {
  
  // Method decorator & Parameter decorator
  @Post()
  create(@Body() createUserDto: any) {
    return 'This action adds a new user';
  }

  // Composing decorators (Guard + Route handler)
  @Get()
  @UseGuards(AuthGuard)
  findAll() {
    return 'This action returns all users';
  }
}

Creating a Custom Decorator

It is very common to create a custom @User() decorator to easily extract the authenticated user entity from the request object.

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

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    // Assuming the AuthGuard attached the user to the request object
    return request.user; 
  },
);

Using the Custom Decorator:

@Get('profile')
getProfile(@User() user: UserEntity) {
  return `Hello, ${user.firstName}!`;
}

Best Practices

  • Create Custom Param Decorators: Instead of using @Req() and pulling req.user out manually in every controller, create a custom @User() decorator. This keeps controllers clean, DRY, and easier to type-check.
  • Don’t Overuse Class/Method Decorators: While it’s tempting to build complex custom method decorators for business logic, remember that decorators run outside of the DI context (mostly). It’s usually better to use Interceptors or Guards for complex request flow logic.