Bird
Raised Fist0
NestJSframework~10 mins

Reflector and custom decorators in NestJS - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Reflector and custom decorators
Define Custom Decorator
↓
Attach Metadata to Target
↓
Use Reflector in Guard/Interceptor
↓
Read Metadata from Target
↓
Make Decisions Based on Metadata
↓
Allow or Deny Access / Modify Behavior
This flow shows how a custom decorator adds metadata to a target, and how Reflector reads that metadata to influence behavior.
Execution Sample
NestJS
import { SetMetadata } from '@nestjs/common';

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

// In a guard
const roles = this.reflector.get<string[]>('roles', context.getHandler());
Defines a custom decorator 'Roles' that attaches roles metadata, then Reflector reads it in a guard to check access.
Execution Table
StepActionTargetMetadata KeyMetadata ValueResult
1Call Roles('admin', 'user') decoratorController method'roles'['admin', 'user']Metadata attached to method
2Request triggers guardRequest context--Guard starts execution
3Reflector reads metadataController method'roles'['admin', 'user']Roles metadata retrieved
4Guard compares user rolesUser roles-['user']User has 'user' role
5Decision based on roles---Access granted because 'user' role matches
6Request proceeds to handler---Handler executes
7End---Execution complete
💡 Execution stops after handler runs or access denied if roles don't match
Variable Tracker
VariableStartAfter Step 1After Step 3After Step 4Final
metadataKeyundefined'roles''roles''roles''roles'
metadataValueundefined['admin', 'user']['admin', 'user']['admin', 'user']['admin', 'user']
userRolesundefinedundefinedundefined['user']['user']
accessGrantedfalsefalsefalsetruetrue
Key Moments - 3 Insights
Why does the Reflector need both the metadata key and the target (like method) to get metadata?
Because metadata is attached to specific targets (methods or classes), Reflector must know where to look. Step 3 shows Reflector reading 'roles' metadata from the controller method.
What happens if the user roles do not match the roles metadata?
In Step 5, the guard would deny access, stopping the request before the handler runs. This is why the decision step is crucial.
Can custom decorators attach any kind of data as metadata?
Yes, custom decorators can attach any data type as metadata using SetMetadata, as shown in Step 1 attaching an array of strings.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the metadata value retrieved by Reflector at Step 3?
Aundefined
B['user']
C['admin', 'user']
D['guest']
💡 Hint
Check the 'Metadata Value' column at Step 3 in the execution table.
At which step does the guard decide to allow access based on user roles?
AStep 5
BStep 2
CStep 4
DStep 6
💡 Hint
Look at the 'Result' column describing the decision in the execution table.
If the user roles were ['guest'] instead of ['user'], what would change in the execution table?
AReflector would fail to read metadata
BStep 5 would deny access instead of granting it
CMetadata value would change at Step 3
DHandler would execute anyway
💡 Hint
Consider the 'Result' column at Step 5 and how user roles affect access.
Concept Snapshot
Custom decorators use SetMetadata to attach data to classes or methods.
Reflector reads this metadata during runtime.
Guards or interceptors use Reflector to get metadata and decide behavior.
This enables flexible, reusable access control or behavior modification.
Always specify metadata key and target when using Reflector.
Full Transcript
This lesson shows how to create a custom decorator in NestJS that attaches metadata to a controller method using SetMetadata. The Reflector class is then used in a guard to read this metadata at runtime. The guard compares the metadata roles with the user's roles to decide if access should be granted. The execution table traces each step: attaching metadata, triggering the guard, reading metadata, checking user roles, making a decision, and finally allowing the request to proceed or denying it. Variables like metadataKey, metadataValue, userRoles, and accessGranted change as the code runs. Key moments clarify why Reflector needs the target to read metadata, what happens if roles don't match, and that custom decorators can attach any data. The quiz tests understanding of metadata retrieval, decision steps, and effects of different user roles. This visual execution helps beginners see how decorators and Reflector work together in NestJS to control access and behavior dynamically.

Practice

(1/5)
1. What is the primary purpose of the Reflector class in NestJS when used with custom decorators?
easy
A. To read metadata set by custom decorators on classes or methods
B. To create new decorators dynamically at runtime
C. To replace the default routing mechanism in controllers
D. To automatically validate request payloads

Solution

  1. Step 1: Understand Reflector's role

    The Reflector class is designed to read metadata that custom decorators attach to classes or methods.
  2. Step 2: Differentiate from other functions

    It does not create decorators, change routing, or validate payloads; it only reads metadata.
  3. Final Answer:

    To read metadata set by custom decorators on classes or methods -> Option A
  4. Quick Check:

    Reflector reads metadata = D [OK]
Hint: Reflector always reads metadata, never creates or changes routes [OK]
Common Mistakes:
  • Thinking Reflector creates decorators
  • Confusing Reflector with validation tools
  • Assuming Reflector changes routing behavior
2. Which of the following is the correct way to define a custom decorator that sets metadata with key 'roles' and value passed as argument?
easy
A. export function Roles() { return SetMetadata('roles'); }
B. export function Roles(roles: string[]) { return Reflect.metadata('roles', roles); }
C. export const Roles = (roles: string[]) => Reflector.set('roles', roles);
D. export const Roles = (roles: string[]) => SetMetadata('roles', roles);

Solution

  1. Step 1: Recall SetMetadata usage

    SetMetadata is the NestJS helper to create decorators that attach metadata with a key and value.
  2. Step 2: Check each option

    export const Roles = (roles: string[]) => SetMetadata('roles', roles); correctly returns SetMetadata with key 'roles' and the passed roles array. export function Roles(roles: string[]) { return Reflect.metadata('roles', roles); } uses Reflect.metadata which is not standard in NestJS. export const Roles = (roles: string[]) => Reflector.set('roles', roles); incorrectly calls Reflector.set which does not exist. export function Roles() { return SetMetadata('roles'); } calls SetMetadata without value, which is invalid.
  3. Final Answer:

    export const Roles = (roles: string[]) => SetMetadata('roles', roles); -> Option D
  4. Quick Check:

    SetMetadata creates decorators with key-value metadata = A [OK]
Hint: Use SetMetadata(key, value) to create custom decorators [OK]
Common Mistakes:
  • Using Reflector to set metadata instead of reading
  • Omitting the value argument in SetMetadata
  • Using Reflect.metadata instead of SetMetadata
3. Given this code snippet, what will be logged when accessing the getData method?
import { Controller, Get, UseGuards, SetMetadata } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

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

@Controller('items')
export class ItemsController {
  @Get()
  @Roles(['admin'])
  getData() {
    return 'secret data';
  }
}

// In a guard:
const reflector = new Reflector();
const roles = reflector.get<string[]>('roles', ItemsController.prototype.getData);
console.log(roles);
medium
A. ['admin']
B. undefined
C. An error because metadata key is missing
D. [] (empty array)

Solution

  1. Step 1: Understand metadata retrieval context

    Reflector.get requires the target object and the property key (method name as string) to read metadata.
  2. Step 2: Analyze the call

    The code calls reflector.get with ItemsController.prototype.getData as the second argument, which is a function, not the method name string 'getData'. This causes Reflector to fail to find metadata and return undefined.
  3. Final Answer:

    undefined -> Option B
  4. Quick Check:

    Reflector.get needs method name string, else returns undefined [OK]
Hint: Pass method name as string to Reflector.get to read metadata [OK]
Common Mistakes:
  • Passing method function instead of method name string
  • Expecting metadata on class prototype instead of method
  • Assuming metadata always returns array
4. Identify the error in this custom decorator usage and how to fix it:
import { SetMetadata } from '@nestjs/common';

function Permissions(permissions: string[]) {
  return (target: any, key: string) => {
    Reflect.defineMetadata('permissions', permissions, target, key);
  };
}

class UserController {
  @Permissions(['read'])
  getUser() {
    return 'user';
  }
}
medium
A. The decorator must be applied to the class, not method
B. The decorator is missing the return type annotation
C. The decorator should use SetMetadata instead of Reflect.defineMetadata
D. The decorator function parameters are incorrect; should include descriptor

Solution

  1. Step 1: Check decorator implementation

    The decorator manually calls Reflect.defineMetadata, which works but is not recommended in NestJS.
  2. Step 2: Use NestJS helper for consistency

    SetMetadata is the standard NestJS helper that wraps Reflect.defineMetadata and ensures compatibility with Reflector and other NestJS internals.
  3. Final Answer:

    The decorator should use SetMetadata instead of Reflect.defineMetadata -> Option C
  4. Quick Check:

    Use SetMetadata for custom decorators in NestJS [OK]
Hint: Always use SetMetadata helper for custom decorators [OK]
Common Mistakes:
  • Using Reflect.defineMetadata directly instead of SetMetadata
  • Ignoring NestJS decorator conventions
  • Misunderstanding decorator parameters
5. You want to create a guard that allows access only if the user has at least one role from the @Roles decorator metadata. Which approach correctly uses Reflector and the custom decorator to achieve this?
hard
A. Use Reflector.getAllAndOverride('roles', [context.getHandler(), context.getClass()]) to get roles, then check user roles against it
B. Use Reflector.get('roles', context.getHandler()) only, ignoring class-level metadata
C. Manually parse request headers for roles instead of using Reflector
D. Use Reflector.set('roles', roles) inside the guard to assign roles dynamically

Solution

  1. Step 1: Understand metadata inheritance

    Roles can be set on method or class level. To get effective roles, use Reflector.getAllAndOverride which checks method first, then class.
  2. Step 2: Implement guard logic

    Retrieve roles with getAllAndOverride, then compare with user's roles to allow or deny access.
  3. Final Answer:

    Use Reflector.getAllAndOverride('roles', [context.getHandler(), context.getClass()]) to get roles, then check user roles against it -> Option A
  4. Quick Check:

    getAllAndOverride merges metadata from method and class [OK]
Hint: Use getAllAndOverride to combine method and class metadata [OK]
Common Mistakes:
  • Ignoring class-level metadata roles
  • Trying to set metadata inside guard
  • Not using Reflector to read metadata
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard