NestJS is a powerful framework for building efficient, reliable, and scalable server-side applications. It leverages TypeScript and provides a robust set of tools to handle common tasks like routing, dependency injection, and more. One of these tools is decorators. In this article, we’ll explore how to create custom decorators in NestJS, which can help you extend and enrich your application in a clean and reusable way.
What Are Decorators?
Decorators are a special kind of declaration that can be attached to classes, methods, accessor properties, or parameter properties. They allow for adding additional metadata or modifying the behavior of the said elements. In NestJS, decorators are extensively used, such as @Controller()
, @Get()
, @Post()
, @Injectable()
, etc.
Why Custom Decorators?
Custom decorators can be immensely useful when you want to encapsulate and reuse a piece of logic across your application. For example, you might want to create a decorator to automatically parse and validate request headers or to add role-based access control to methods.
Creating a Simple Custom Decorator
First, let’s create a simple custom decorator to log the execution time of a method. This will help you understand the basic structure of a decorator in NestJS.
Step 1: Setting Up Your NestJS Project
If you haven’t set up a NestJS project yet, you can do so by running the following commands:
npm i -g @nestjs/cli
nest new my-nest-project
cd my-nest-project
Step 2: Create the Decorator Function
Let’s create a file named log-execution-time.decorator.ts
and add the following code:
import { Logger } from '@nestjs/common';
export function LogExecutionTime() {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalMethod = descriptor.value;
descriptor.value = async function(...args: any[]) {
const startTime = Date.now();
const result = await originalMethod.apply(this, args);
const endTime = Date.now();
Logger.log(
`Execution time of ${propertyKey}: ${endTime - startTime}ms`,
);
return result;
};
};
}
Step 3: Apply the Decorator
Now that we have our custom decorator, let’s use it in a service. Open your app.service.ts
and modify it as follows:
import { Injectable } from '@nestjs/common';
import { LogExecutionTime } from './log-execution-time.decorator';
@Injectable()
export class AppService {
@LogExecutionTime()
async getHello(): Promise<string> {
// Simulate a delay
await new Promise((resolve) => setTimeout(resolve, 1000));
return 'Hello, World!';
}
}
Step 4: Run the Application
Start your application by running:
npm run start
When you visit the endpoint that triggers getHello()
, you should see a log entry in your console showing the execution time:
[Nest] 12345 - Execution time of getHello: 1001ms
Advanced Custom Decorators
Apart from method decorators, you can also create property, class, and parameter decorators. Let’s create a more advanced example: a custom decorator to extract user information from a request.
Step 1: User Decorator
Create a new file user.decorator.ts
and write the following code:
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
Step 2: Use the User Decorator
Apply the new decorator in a controller. Open app.controller.ts
and modify it:
import { Controller, Get } from '@nestjs/common';
import { User } from './user.decorator';
@Controller()
export class AppController {
@Get('profile')
getProfile(@User() user: any): string {
console.log(user);
return `User Profile: ${JSON.stringify(user)}`;
}
}
Step 3: Extend Request Interface
For this to work correctly, particularly if you type your code strictly, extend the Request interface (generally from Express) to include the user property. Add the following to src/types/express.d.ts
:
import { User } from '../user.entity'; // adapt this import to match your User entity
declare module 'express' {
export interface Request {
user?: User;
}
}
Conclusion
Custom decorators are a powerful feature in NestJS that allows you to encapsulate and reuse common logic across your application. Whether you need to log execution time, extract user information, or perform any other repetitive task, decorators offer a clean and maintainable way to achieve it.
By mastering custom decorators, you’ll be able to write more modular and maintainable code, making your NestJS applications even more powerful and efficient.
Happy coding!