Skip to content

NestJS Custom Decorators

Custom decorators reduce boilerplate—extracting the current user from a request, or attaching roles metadata. This lesson covers parameter decorators and metadata decorators.

Parameter Decorators With createParamDecorator

createParamDecorator builds decorators like @CurrentUser() that read from ExecutionContext and supply a value to a handler parameter, keeping controllers free of req.user casting.

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

@Get('me')
me(@CurrentUser() user: AuthUser) {
  return user;
}

After an auth guard attaches request.user, @CurrentUser() exposes it as a typed argument.

Metadata Decorators With SetMetadata

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

@Roles('admin')
@Delete(':id')
remove() {}
  • Parameter decorators extract request data; metadata decorators annotate handlers for guards/interceptors.
  • Read metadata with Reflector inside guards.
  • Keep decorator factories small and composable.
  • Document custom decorators so teams use them consistently.

Custom Decorator Cheatsheet

Two decorator styles in Nest.

Style API Use
Param createParamDecorator Inject derived request values
Metadata SetMetadata Mark routes for guards/filters
Combine applyDecorators Compose multiple decorators
Read meta Reflector Consume metadata in guards

Composing Decorators

applyDecorators packages common combinations (@Auth() = JwtAuthGuard + Roles) into one readable decorator for controllers.

export function Auth(...roles: string[]) {
  return applyDecorators(UseGuards(JwtAuthGuard, RolesGuard), Roles(...roles));
}

Works Beyond HTTP

Parameter decorators can switch on context type to support GraphQL context users as well as HTTP requests.

Common Mistakes

  • Reading request.user in many controllers instead of a shared @CurrentUser() decorator.
  • Forgetting Reflector when writing a RolesGuard that expects metadata.
  • Building huge decorators that hide important security behavior.
  • Not typing the value returned by a param decorator.

Key Takeaways

  • createParamDecorator builds clean parameter extractors.
  • SetMetadata + Reflector power declarative guards.
  • applyDecorators composes common auth/role stacks.
  • Custom decorators improve readability when used consistently.

Pro Tip

Create an @Auth() composed decorator for protected routes so controllers declare intent in one place instead of repeating guard lists.