How to create and use custom decorators in NestJS?
Custom Decorators in NestJS
NestJS extensively uses TypeScript decorators for metadata-driven development. You can create your own decorators to reduce boilerplate and add reusable behavior.
Types of Custom Decorators
1. Parameter Decorator
Extract data from the request:
typescript
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);Usage:
typescript
@Controller('profile')
export class ProfileController {
@Get()
getProfile(@CurrentUser() user: User) {
return user;
}
@Get('email')
getEmail(@CurrentUser('email') email: string) {
return { email };
}
}2. Metadata Decorator with SetMetadata
typescript
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
export const Public = () => SetMetadata('isPublic', true);Used with Guards:
typescript
@Controller('admin')
export class AdminController {
@Get('dashboard')
@Roles('admin', 'superadmin')
getDashboard() {
return { message: 'Admin dashboard' };
}
@Get('health')
@Public()
healthCheck() {
return { status: 'ok' };
}
}3. Composed Decorator
Combine multiple decorators into one:
typescript
import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';
import { ApiBearerAuth, ApiUnauthorizedResponse } from '@nestjs/swagger';
export function Auth(...roles: string[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(JwtAuthGuard, RolesGuard),
ApiBearerAuth(),
ApiUnauthorizedResponse({ description: 'Unauthorized' }),
);
}Usage — clean and DRY:
typescript
@Controller('users')
export class UsersController {
@Get()
@Auth('admin')
findAll() {
return this.usersService.findAll();
}
}4. Class Decorator
typescript
import { Controller, UseInterceptors } from '@nestjs/common';
export function ApiController(prefix: string) {
return applyDecorators(
Controller(`api/v1/${prefix}`),
UseInterceptors(LoggingInterceptor),
UseInterceptors(TransformInterceptor),
);
}typescript
@ApiController('users')
export class UsersController {
// All routes prefixed with /api/v1/users
// LoggingInterceptor and TransformInterceptor applied
}5. Property Decorator (for Validation)
typescript
import { registerDecorator, ValidationOptions } from 'class-validator';
export function IsStrongPassword(validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'isStrongPassword',
target: object.constructor,
propertyName,
options: validationOptions,
validator: {
validate(value: string) {
return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&]).{8,}$/.test(value);
},
defaultMessage() {
return 'Password must contain uppercase, lowercase, number, and special character';
},
},
});
};
}Summary
| Decorator Type | Use Case | API |
|---|---|---|
| Parameter | Extract request data | createParamDecorator() |
| Metadata | Set metadata for Guards/Interceptors | SetMetadata() |
| Composed | Combine multiple decorators | applyDecorators() |
| Class | Apply to entire controller/class | applyDecorators() |
| Property | Custom validation rules | registerDecorator() |
Best practice: Custom decorators are powerful for reducing boilerplate. Create
@Auth(),@CurrentUser(), and@Public()decorators in every NestJS project — they're used constantly.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.