Decoradores personalizados

Intermedio
Nest
Nest
Actualizado: 05/05/2026

Diagrama: tutorial-nest-decorators-personalizados

Decoradores personalizados

Los decoradores personalizados en NestJS permiten crear funcionalidades reutilizables que encapsulan lógica específica de tu aplicación. Mientras que los decoradores integrados como @Body() o @Param() cubren casos comunes, los decoradores personalizados te permiten extraer y procesar datos de manera específica según las necesidades de tu proyecto.

NestJS proporciona la función createParamDecorator() que facilita la creación de estos decoradores. Esta función recibe dos parámetros: los datos opcionales que se pasan al decorador y el contexto de ejecución que contiene información sobre la petición actual.

Creación básica de un decorador personalizado

Para crear un decorador personalizado, utilizamos la función createParamDecorator() junto con el contexto de ejecución:

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

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

Este decorador @User() extrae automáticamente el objeto usuario de la petición, simplificando el acceso a esta información en los controladores:

@Controller('profile')
export class ProfileController {
  @Get()
  getProfile(@User() user: any) {
    return user;
  }
}

Decoradores con parámetros específicos

Los decoradores personalizados pueden recibir parámetros para extraer propiedades específicas del objeto. El parámetro data contiene el valor pasado al decorador:

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

Ahora puedes usar el decorador para extraer propiedades específicas del usuario:

@Controller('profile')
export class ProfileController {
  @Get('name')
  getUserName(@User('name') userName: string) {
    return { name: userName };
  }
  
  @Get('email')
  getUserEmail(@User('email') email: string) {
    return { email };
  }
}

Decoradores para validación y transformación

Los decoradores personalizados también pueden realizar validaciones y transformaciones de datos antes de pasarlos al método del controlador:

import { BadRequestException } from '@nestjs/common';

export const ValidatedUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;
    
    if (!user) {
      throw new BadRequestException('Usuario no autenticado');
    }
    
    if (!user.isActive) {
      throw new BadRequestException('Usuario inactivo');
    }
    
    return {
      id: user.id,
      email: user.email,
      role: user.role,
      lastLogin: new Date(user.lastLogin)
    };
  },
);

Decoradores para extracción de headers

Un caso común es crear decoradores para extraer información específica de los headers de la petición:

export const ApiKey = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const apiKey = request.headers['x-api-key'];
    
    if (!apiKey) {
      throw new BadRequestException('API Key requerida');
    }
    
    return apiKey;
  },
);

export const UserAgent = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.headers['user-agent'];
  },
);

Estos decoradores simplifican el acceso a información específica de los headers:

@Controller('api')
export class ApiController {
  @Get('data')
  getData(
    @ApiKey() apiKey: string,
    @UserAgent() userAgent: string
  ) {
    return {
      message: 'Datos obtenidos correctamente',
      apiKey,
      userAgent
    };
  }
}

Combinación con pipes

Los decoradores personalizados funcionan perfectamente con pipes para realizar validaciones y transformaciones adicionales:

export const UserId = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user?.id;
  },
);
import { ParseIntPipe } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get('posts')
  getUserPosts(@UserId(ParseIntPipe) userId: number) {
    return this.postsService.findByUserId(userId);
  }
}

Decoradores para contextos específicos

También puedes crear decoradores que funcionen en contextos específicos como WebSockets o microservicios:

export const SocketUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const client = ctx.switchToWs().getClient();
    return client.user;
  },
);

export const RpcData = createParamDecorator(
  (data: string, ctx: ExecutionContext) => {
    const rpcContext = ctx.switchToRpc();
    const payload = rpcContext.getData();
    
    return data ? payload[data] : payload;
  },
);

Los decoradores personalizados mejoran la legibilidad del código y promueven la reutilización de lógica común. Al encapsular la extracción y validación de datos en decoradores específicos, reduces la duplicación de código y mantienes los métodos de los controladores enfocados en su lógica principal.

Decoradores de método con SetMetadata y Reflector

createParamDecorator cubre el caso de los parámetros. Para anotar handlers o controladores enteros con metadatos consumidos por guards o interceptors se usa SetMetadata o Reflector.createDecorator. El patrón típico de autorización:

import { Reflector } from '@nestjs/core';

export const Roles = Reflector.createDecorator<string[]>();

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

  canActivate(context: ExecutionContext): boolean {
    const required = this.reflector.getAllAndOverride(Roles, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!required?.length) return true;

    const { user } = context.switchToHttp().getRequest();
    return required.some((role) => user?.role === role);
  }
}

@Controller('admin')
@UseGuards(RolesGuard)
export class AdminController {
  @Get('users')
  @Roles(['admin', 'superadmin'])
  listUsers() { /* ... */ }
}

Reflector.createDecorator<T>() (NestJS 10+) genera un decorador tipado y un token consumible por getAllAndOverride, eliminando los strings mágicos del antiguo SetMetadata('roles', [...]).

Composición de decoradores con applyDecorators

Cuando varios decoradores siempre van juntos (autenticación + autorización + Swagger), applyDecorators los combina en un único decorador semántico:

import { applyDecorators, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiUnauthorizedResponse, ApiForbiddenResponse } from '@nestjs/swagger';

export function Auth(...roles: string[]) {
  return applyDecorators(
    Roles(roles),
    UseGuards(JwtAuthGuard, RolesGuard),
    ApiBearerAuth(),
    ApiUnauthorizedResponse({ description: 'Token inválido o ausente' }),
    ApiForbiddenResponse({ description: 'Rol insuficiente' }),
  );
}

@Controller('orders')
export class OrdersController {
  @Post()
  @Auth('admin', 'manager')
  create(@Body() dto: CreateOrderDto) { /* ... */ }
}

Un solo @Auth('admin') reemplaza cinco decoradores y mantiene la documentación OpenAPI alineada con la lógica de seguridad. Es la forma profesional de evitar olvidos en endpoints sensibles.

Decorador con factory para mensajes contextuales

Los decoradores también admiten factories que reciben argumentos en tiempo de definición y devuelven un decorador específico. Esto permite generar variantes del mismo comportamiento:

export const RequestField = (field: keyof Request) =>
  createParamDecorator((_, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest<Request>();
    return request[field];
  })();

@Get('debug')
debug(@RequestField('ip') ip: string, @RequestField('hostname') host: string) {
  return { ip, host };
}

Buenas prácticas

  • Evita lógica de negocio en decoradores: el decorador extrae y valida; las reglas del dominio van en servicios.
  • Tipa fuertemente los argumentos: createParamDecorator<string, ExecutionContext, User> da autocompletado real.
  • Documenta cada decorador con /** */ para que aparezca en hover de IDE.
  • Tests unitarios: simula ExecutionContext con createMock<ExecutionContext>() (de @golevelup/ts-jest) y comprueba la extracción y los lanzamientos de BadRequestException.

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en Nest

Documentación oficial de Nest
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Nest es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Nest

Explora más contenido relacionado con Nest y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Comprender qué son los decoradores personalizados en NestJS y su utilidad. Aprender a crear decoradores personalizados básicos usando createParamDecorator(). Saber cómo pasar parámetros a decoradores para extraer datos específicos. Implementar validaciones y transformaciones dentro de decoradores personalizados. Aplicar decoradores personalizados en diferentes contextos, incluyendo HTTP, WebSockets y microservicios.