Fullstack CourseLearn by building
Back to week 1

Topic

Decorators

Definition

A decorator is a function applied to a class, method, property, or parameter with an @ prefix that attaches metadata Nest reads at runtime to change how that target is treated.

In simpler words

Decorators are labels you stick on code — @Controller, @Get, @Injectable — that tell Nest what the labeled thing is for.

Nest is built almost entirely from decorator metadata: which class is a controller, which method handles which route, which parameter is public/role-gated. A Nest app commonly defines its own decorators for auth.

After this you can

  • Classify a decorator as class-level, method-level, or parameter-level
  • Explain @Public(), @Roles(...), and @CurrentUser() and know what each attaches
  • Explain SetMetadata vs createParamDecorator
  • Predict what happens if a decorator is left off a route

Kinds of decorators Nest uses

Definition

Nest decorators attach metadata at the class level (@Controller, @Injectable, @Module), the method level (@Get, @Post, @Roles), or the parameter level (@Body, @Param, @CurrentUser), and Nest inspects that metadata when handling a request.

In simpler words

Class decorators say what the class is; method decorators say what a route does; parameter decorators say what to hand a method argument.

A class decorator like @Controller("tickets") or @Injectable() marks the whole class’s role in the DI/routing system.

A method decorator like @Get(":id") or @Roles("admin") attaches route or access metadata to one handler.

A parameter decorator like @Param("id") or @CurrentUser() tells Nest how to extract a value from the request and pass it as that argument.

All three kinds on one controller

@Controller('tickets')          // class decorator
export class TicketsController {
  @Delete(':id')                // method decorator
  @Roles('admin')               // method decorator (custom)
  remove(@Param('id') id: string) {  // parameter decorator
    return this.ticketsService.remove(id);
  }
}

Three decorator kinds, three different jobs, all metadata Nest reads before your method body runs.

Custom auth decorators

Definition

A custom decorator can be built from SetMetadata to attach arbitrary route metadata, or from createParamDecorator to compute and return a value from the current ExecutionContext.

In simpler words

@Public() and @Roles(...) just stick a metadata flag on a route; @CurrentUser() reaches into the request and hands back the logged-in user.

A common auth.decorators file defines Public = () => SetMetadata(IS_PUBLIC_KEY, true) and Roles = (...roles) => SetMetadata(ROLES_KEY, roles) — both are metadata flags that guards later read with a Reflector.

CurrentUser is built with createParamDecorator: it reads req.user (attached by the JWT strategy after a guard authenticates the request) and returns it directly as a typed User parameter.

Decorator definitions

export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
export const Roles = (...roles: Array<'admin' | 'member'>) =>
  SetMetadata(ROLES_KEY, roles);

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

JwtAuthGuard/RolesGuard read IS_PUBLIC_KEY and ROLES_KEY with Reflector; CurrentUser just projects request.user.

Mistake: expecting @CurrentUser() on a public route

// Wrong — no guard ran, request.user was never set
@Public()
@Get('whoami')
me(@CurrentUser() user: User) { return user; } // undefined

// Right — protected route, guard populates request.user first
@Get('whoami')
me(@CurrentUser() user: User) { return user; }

@CurrentUser() only works after an auth guard has run and attached request.user — @Public() skips that guard.

Keep in mind

  • A decorator with no code reading its metadata does nothing — always check which guard/pipe/interceptor consumes it.
  • Prefer existing decorators (@Public, @Roles, @CurrentUser) over re-deriving auth state manually in a controller.
  • Order matters for stacked method decorators in some cases (e.g. guards run in registration order) — keep it consistent with existing controllers.

Test

Check your understanding

At least 10 questions — mix of concept, syntax, practical, and logic. Score ≥80% (enforced by the API) to save progress.

Checking your session…

15 questions · concept 5 · syntax 3 · practical 4 · logic 3

1. What do Nest decorators primarily do?
Concept
2. What does @Public() do in this codebase?
Syntax
3. What do @Roles(...) decorators do?
Practical
4. Where can parameter decorators like @CurrentUser() apply?
Logic
5. Which decorator maps a GET route?
Concept
6. Who actually rejects unauthorized requests?
Practical
7. What is SetMetadata used for?
Syntax
8. Which claim about @Roles execution is most accurate?
Logic
9. What does @Body() do?
Concept
10. Why combine decorators with Reflector in guards?
Practical
11. What does applying @Public() to this route actually do?
Conceptintermediate
export const Public = () => SetMetadata('isPublic', true);

@Public()
@Get('health')
health() { return { ok: true }; }
12. What will user be inside this handler?
Syntaxadvanced
export const CurrentUser = createParamDecorator(
  (_data, ctx: ExecutionContext) =>
    ctx.switchToHttp().getRequest().user,
);

@Get('me')
me(@CurrentUser() user: User) { return user; }
13. What does @Roles by itself do here?
Practicalintermediate
@Roles('admin')
@Delete(':id')
remove() {}
14. What is the relationship between these decorators?
Logicadvanced
@UseGuards(RolesGuard)
@Roles('admin')
@Get()
list() {}
15. What does @Body() do here?
Conceptintermediate
@Post()
create(@Body() dto: CreateTicketDto) {}

Checking your session…