Read Metadata Set by Decorators
ExecutionContext & Reflector
ExecutionContext is the "where am I?" object guards and interceptors receive. Reflector reads custom metadata attached by decorators.
What you'll learn
- Get the request, handler, and class from ExecutionContext
- Read metadata with Reflector.get and getAllAndOverride
- Set custom metadata with SetMetadata or a typed factory
Guards, interceptors, and filters all receive an ExecutionContext. It’s
how they ask Nest: which handler is this? which controller? what
transport — HTTP, RPC, WebSocket? Combined with Reflector, it powers
every metadata-driven pattern in Nest, including the classic roles guard.
What ExecutionContext Gives You
canActivate(ctx: ExecutionContext) {
// Transport-specific accessors
const req = ctx.switchToHttp().getRequest();
const res = ctx.switchToHttp().getResponse();
// Who is being called
const handler = ctx.getHandler(); // the method (a Function)
const controller = ctx.getClass(); // the controller class
const args = ctx.getArgs(); // raw transport args
return true;
} switchToHttp() is the right entry for REST. The same context object also
has switchToRpc() and switchToWs() so the same guard can serve
microservices or sockets.
Setting Custom Metadata
Use SetMetadata(key, value) — or, better, wrap it in a typed decorator.
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles); Now any route can declare its required roles:
@Delete(':id')
@Roles('admin')
remove(@Param('id') id: string) {
return this.users.remove(id);
} Reading It With Reflector
Inject Reflector and ask for the key. The handy getAllAndOverride
checks the method first, then falls back to the controller — exactly what
you want for “method overrides class” semantics.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(ctx: ExecutionContext): boolean {
const required = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
ctx.getHandler(),
ctx.getClass(),
]);
if (!required?.length) return true; // no decorator -> open
const { user } = ctx.switchToHttp().getRequest();
return required.some((role) => user?.roles?.includes(role));
}
} Apply the guard globally and routes opt in to protection just by adding
@Roles(...).
The Pattern in General
This decorator + reflector + guard triangle is how Nest builds
declarative features:
- A decorator (
@Roles,@Public,@RateLimit) attaches metadata. - A guard or interceptor reads it from
ExecutionContextviaReflector. - The handler stays clean — the rule lives next to the route definition.
Most third-party Nest libraries (passport, throttler, swagger) work the same way. Once you’ve written one, you’ll spot the pattern everywhere.
ExecutionContext vs ArgumentsHost
A small naming note: exception filters receive ArgumentsHost instead of
ExecutionContext. ExecutionContext extends ArgumentsHost with
getHandler and getClass, which filters don’t always have access to.
Otherwise they behave identically.
This wraps up the pipeline section. With pipes, guards, interceptors, filters, and metadata, you’ve got every Nest tool for shaping the request lifecycle.
Persistence With TypeORM →