Custom Decorators
Custom Decorators allow you to cleanly extract data from the request object and inject it directly into your controller methods, reducing boilerplate and improving type safety.
Overview
After a user is authenticated, their data is attached to the Express/Fastify request object (specifically, req.user).
In a controller method, you could access this by injecting the entire request: @Req() req: Request, and then pulling out req.user. However, this tightly couples your controller to the underlying HTTP framework, makes testing harder, and requires you to manually type-cast req.user every time.
Creating a custom @CurrentUser() decorator is the idiomatic NestJS solution.
Key Concepts
createParamDecorator: A NestJS utility function that abstracts away the complexity of building decorators that read from theExecutionContext.- Parameter Injection: The decorator is used exactly like
@Body()or@Query(), but it injects whatever data you tell it to extract from the request.
Code Examples
1. Creating the @CurrentUser Decorator
This decorator looks at the request object, finds the user property (which was populated by the Authentication Guard), and returns it.
// current-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
// The 'data' parameter allows you to pass arguments to the decorator, e.g., @CurrentUser('id')
(data: string | undefined, ctx: ExecutionContext) => {
// Switch to the HTTP context to access the raw request
const request = ctx.switchToHttp().getRequest();
const user = request.user;
// If a specific property was requested (like 'id'), return just that property.
// Otherwise, return the entire user object.
return data ? user?.[data] : user;
},
);
2. Using the Decorator in a Controller
Look how clean the controller method becomes! We don’t need to import Express’s Request object anymore.
// profile.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './jwt-auth.guard';
import { CurrentUser } from './current-user.decorator';
// Assume this interface matches the payload returned by your JwtStrategy
interface UserPayload {
id: string;
email: string;
role: string;
}
@Controller('profile')
@UseGuards(JwtAuthGuard)
export class ProfileController {
@Get()
// Inject the entire user object and strongly type it!
getProfile(@CurrentUser() user: UserPayload) {
return `Hello, ${user.email}`;
}
@Get('settings')
// Use the 'data' parameter to extract ONLY the ID string!
getSettings(@CurrentUser('id') userId: string) {
return this.settingsService.findByUserId(userId);
}
}
Best Practices
- Type Safety: The decorator itself cannot easily provide strong typing in the controller signature. You should define an Interface/Type for your JWT Payload (or User Entity) and explicitly apply it in the controller method signature (
@CurrentUser() user: UserPayload), as shown in the example above. - The
@Public()Decorator: Another highly recommended custom decorator is@Public(). Instead of usingcreateParamDecorator, it usesSetMetadata. You apply it to routes like/loginor/register, and configure your global Authentication Guard to read the metadata; if the metadata says “Public”, the guard immediately returnstruewithout checking for a token.