Pull Anything Out of the Request With One Line
Custom Parameter Decorators
`createParamDecorator` lets you build reusable decorators like `@CurrentUser()` that hide the plumbing.
What you'll learn
- Create a parameter decorator with `createParamDecorator`
- Read from the request inside the factory
- Compose custom decorators with pipes
Built-in decorators (@Body, @Param, @Query) cover the obvious bits
of a request. For anything project-specific — the authenticated user,
the tenant ID, a parsed cookie — you can write your own.
Building @CurrentUser()
A guard normally attaches the user to the request object:
req.user = {...}. Repeating req.user in every handler is noise. A
custom decorator fixes that:
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export interface CurrentUserPayload {
id: number;
email: string;
roles: string[];
}
export const CurrentUser = createParamDecorator(
(_data: unknown, ctx: ExecutionContext): CurrentUserPayload => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
); createParamDecorator takes a factory that runs at request time. It
receives any args passed to the decorator (data) and the
ExecutionContext, from which you can pull the underlying request.
Usage feels native:
@Get('me')
me(@CurrentUser() user: CurrentUserPayload) {
return user;
} Accepting an Argument
The factory’s first parameter is whatever the decorator user passes. You can use it to select a sub-field:
export const CurrentUser = createParamDecorator(
(key: keyof CurrentUserPayload | undefined, ctx: ExecutionContext) => {
const user = ctx.switchToHttp().getRequest().user;
return key ? user?.[key] : user;
},
); @Get('me')
email(@CurrentUser('email') email: string) { return email; } Composing With Pipes
Custom decorators stack with pipes exactly like built-ins:
import { ParseIntPipe } from '@nestjs/common';
@Get('mine')
list(@CurrentUser('id', ParseIntPipe) id: number) {
return this.posts.byOwner(id);
} Pipes run after the factory, so you can validate or transform the extracted value before it reaches the handler.
Beyond HTTP
ExecutionContext is transport-agnostic. The same decorator can work
for WebSockets or gRPC if you branch on the context type:
export const Tenant = createParamDecorator((_, ctx: ExecutionContext) => {
if (ctx.getType() === 'http') {
return ctx.switchToHttp().getRequest().tenant;
}
if (ctx.getType() === 'ws') {
return ctx.switchToWs().getClient().data.tenant;
}
return undefined;
}); That’s the same secret behind most well-loved Nest libraries — a small decorator that reaches into the request, returns a clean value, and keeps controllers readable.