Decorators in NestJS
Decorators are the syntax that makes NestJS feel declarative: you annotate a class with @Controller(), a method with @Get(), and a parameter with @Body(), and the framework wires everything up at runtime. Under the hood these rely on the legacy (experimental) decorator implementation plus reflect-metadata, configured through two tsconfig flags. This page surveys the built-in decorator families, explains the compiler setup they require, and shows how to author your own custom parameter decorators with createParamDecorator.
The tsconfig setup decorators need
NestJS uses the pre-Stage-3 decorator flavor so it can emit parameter type metadata — the feature that powers automatic dependency injection. Both flags below are mandatory.
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
experimentalDecorators enables the legacy decorator syntax; emitDecoratorMetadata makes the compiler record each parameter’s type (via reflect-metadata) so Nest can resolve constructor dependencies. The Nest CLI sets both for you, but hand-rolled configs must include them or DI silently breaks.
TypeScript 5.0 shipped standard (Stage 3) decorators, which do not support
emitDecoratorMetadataand have a different signature. NestJS still requires the legacy flavor — do not turnexperimentalDecoratorsoff expecting Stage 3 to work here.
Built-in decorators by target
Nest groups decorators by what they annotate. Knowing the family helps you predict where each applies.
| Target | Examples | Purpose |
|---|---|---|
| Class | @Controller(), @Injectable(), @Module() | register with the framework |
| Method | @Get(), @Post(), @UseGuards() | declare routes & behavior |
| Parameter | @Body(), @Param(), @Query(), @Headers() | extract request data |
| Property | @Inject(), plus class-validator rules on DTOs | inject & validate |
The class and method decorators define structure and routing; parameter decorators pull pieces out of the incoming request so your handler receives plain, typed values.
A typical decorated controller
The decorators compose top to bottom: the class decorator sets the route prefix, the method decorator sets the verb and sub-path, and the parameter decorators bind arguments.
import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common";
@Controller("users")
export class UsersController {
@Get()
list(@Query("page") page: string) {
return { page };
}
@Post()
create(@Body() dto: CreateUserDto) {
return dto;
}
@Get(":id")
findOne(@Param("id") id: string) {
return { id };
}
}
Each parameter decorator reads exactly one slice of the request. Note that @Param/@Query values arrive as strings — combine them with a parse pipe (e.g. ParseIntPipe) to coerce types.
Custom parameter decorators
When you find yourself pulling the same value out of the request in many handlers — the authenticated user, a tenant id, a header — wrap it in a custom decorator with createParamDecorator. The factory receives the decorator’s argument and an ExecutionContext.
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
Now any handler can write @CurrentUser() user: User instead of reaching into request.user by hand. The logic lives in one place, and the handler signature documents the dependency.
Passing data and composing decorators
The first factory argument (data) lets a single decorator be parameterised — for instance, extracting one property of the user. You can also bundle a decorator with guards or interceptors using applyDecorators.
import { applyDecorators, UseGuards } from "@nestjs/common";
export const User = createParamDecorator(
(field: keyof UserEntity | undefined, ctx: ExecutionContext) => {
const user = ctx.switchToHttp().getRequest().user as UserEntity;
return field ? user?.[field] : user;
},
);
// @User() → whole user; @User("email") → just the email
export const Auth = () => applyDecorators(UseGuards(JwtAuthGuard));
@User("email") returns a single typed field thanks to keyof UserEntity, while @Auth() is a reusable composite that applies the JWT guard. Custom decorators also work with pipes: @User("id", ParseIntPipe) validates the extracted value.
Best Practices
- Keep
experimentalDecoratorsandemitDecoratorMetadataenabled; Nest’s DI depends on the metadata. - Do not switch to Stage 3 decorators for Nest code — the framework targets the legacy flavor.
- Reach for
createParamDecoratorwhenever the same request extraction repeats across handlers. - Type the
dataargument (e.g.keyof Entity) so parameterised decorators stay type-safe. - Use
applyDecoratorsto bundle related guards/interceptors into one expressive decorator. - Combine custom param decorators with parse pipes to coerce and validate extracted values.
- Keep decorator factories thin — pull data out, but leave business logic to services.