Using NestJS Custom Decorator – New Updated Guide

by | Nov 8, 2023

NestJS, a popular framework for building web applications with Node.js, may seem complex at first glance, but it offers some fantastic tools that can make your code more organized and powerful. One of these tools is custom decorators. In this article, we will explore NestJS custom decorators, breaking down the concept into simple terms and providing code examples to help you understand their significance and utility.

The Benefits of Embracing Decorators in NestJS

Point 1: Enhancing Loose Coupling

The illustrative code provided above operates independently of specific HTTP server libraries like Express or Fastify. Despite this, a typical NestJS application frequently relies on one of these libraries.

Loose coupling, where one part of the application, such as a controller, remains unaware of other components like the Express server, is a hallmark of well-designed software. While loose coupling comes with its own set of advantages and disadvantages compared to tight coupling, it is generally favored when flexibility is a key requirement.

In the context of NestJS, the beauty lies in the ability to seamlessly switch the underlying HTTP server without altering a single line of your controllers. This independence allows for increased flexibility and adaptability in response to changing requirements.

Point 2: Crafting Cleaner Code

Decorators, being concise one-liners, contribute to code readability and comprehension. However, a word of caution is necessary—relying excessively on decorators can lead to method clutter, with functions nestled under multiple layers of decorators.

As with any coding principle, moderation is key. Decorators should be applied judiciously, serving as a tool for optimizing stable functionalities within your application. Instead of adopting them prematurely in the development process, consider incorporating decorators when you find yourself repeating identical code patterns. This selective use of decorators can significantly enhance the cleanliness and maintainability of your codebase.

Point 3: Streamlining Overall Complexity

Implementing decorators may initially seem like an added layer of complexity. However, the true merit of decorators lies in their ability to encapsulate and abstract away this complexity from your application code. Essentially, you only need to implement decorators once, and their intricacies are shielded from the rest of your codebase.

The key objective here is to shift complexity away from your application code to a separate module or library. By doing so, you free up mental bandwidth to focus on the core and critical aspects of your application. While the journey of mastering decorators may have its challenges, the ultimate payoff is a reduction in the overall complexity of your codebase.

In conclusion, the use of decorators in NestJS introduces a range of advantages, including enhanced flexibility through loose coupling, improved code cleanliness when used judiciously, and a strategic reduction in overall code complexity. As you navigate the realm of decorators, remember that moderation and thoughtful application are essential for reaping the full benefits of this powerful tool.


get in touch to discuss your project

Understanding Custom Decorators

Before diving into code examples, let’s demystify custom decorators. Think of them as labels or markers you can attach to different parts of your code. These labels provide additional functionality or metadata, allowing you to extend and customize your application’s behavior.

Custom decorators are a way to add your own personalized touch to your code, making it more efficient and readable. They can be used to simplify common tasks, such as logging, validation, and authentication, by encapsulating the logic in a reusable manner.

Creating Your NestJS Custom Decorator

Now, let’s create our own custom decorator from scratch. We’ll start with a basic example that demonstrates the power of custom decorators. Imagine you’re building a simple web application for a library, and you want to track when a book was borrowed by a user. We can create a custom decorator to handle this task.

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const BorrowedDate = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.borrowedDate;
  },
);

In this code, we define a custom decorator called BorrowedDate. It uses createParamDecorator to extract the borrowed date from the request object. This borrowed date could be useful for tracking when the book was borrowed by a user.

Using Your Custom Decorator

With the custom decorator in place, we can now use it within a NestJS controller. In this example, we’ll create a BookController with a method that uses our BorrowedDate decorator.

import { Controller, Get, Req } from '@nestjs/common';
import { BorrowedDate } from './borrowed-date.decorator';

@Controller('books')
export class BookController {
  @Get('borrowed')
  getBorrowedDate(@Req() request, @BorrowedDate() borrowedDate: string) {
    return `This book was borrowed on: ${borrowedDate}`;
  }
}

In our BookController, we define a method named getBorrowedDate. By using @BorrowedDate(), we attach our custom decorator to the borrowedDate parameter. When this method is called, it will return the borrowed date.

Advantages of Custom Decorators

Now, let’s explore the advantages of custom decorators:

  1. Code Reusability: Custom decorators allow you to encapsulate common functionality in a reusable manner. This means you can use the same decorator in multiple parts of your application, reducing code duplication.
  2. Cleaner and More Readable Code: Custom decorators make your code cleaner and more readable. By abstracting complex logic into decorators, your controllers and services become more focused on their core responsibilities.

  1. Consistency: Custom decorators promote consistency throughout your application. When you use the same decorator for a specific task, you ensure that the behavior remains consistent across different routes and controllers.

  1. Separation of Concerns: Decorators help separate concerns in your code. Each decorator can be responsible for a specific aspect of your application, such as logging, validation, or authentication.

  1. Testing: Custom decorators are easier to test in isolation. This makes it simpler to write unit tests for the individual parts of your application that use decorators.

Practical Use Cases for Custom Decorators

Now that we’ve covered the basics, let’s explore some practical use cases for custom decorators in NestJS.

1. Logging

You can create a custom decorator for logging HTTP requests and responses. This decorator can log important information, such as the URL, HTTP method, request payload, and response data. It’s a handy tool for debugging and monitoring your application.

export const LogRequest = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const response = ctx.switchToHttp().getResponse();

    // Log request details
    console.log(`Received ${request.method} request at ${request.url}`);
    console.log(`Request data: ${JSON.stringify(request.body)}`);

    // Log response details
    response.on('finish', () => {
      console.log(`Responded with status ${response.statusCode}`);
      console.log(`Response data: ${JSON.stringify(response.body)}`);
    });
  },
);

2. Validation

Custom decorators can be used for input validation. You can create decorators to check if the request contains valid data. If the data is invalid, the decorator can automatically return a validation error response.

export const ValidateInput = (validationSchema: Schema) => {
  return createParamDecorator(
    (data: unknown, ctx: ExecutionContext) => {
      const request = ctx.switchToHttp().getRequest();
      const { value, error } = validationSchema.validate(request.body);

      if (error) {
        throw new BadRequestException('Validation failed', error.message);
      }

      return value;
    },
  );
};

3. Authentication

Implementing authentication in your NestJS application becomes easier with custom decorators. You can create decorators that verify the user’s identity and grant or deny access to certain routes based on their authentication status.

export const Authenticated = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();

    if (!request.user) {
      throw new UnauthorizedException('Authentication failed');
    }

    return request.user;
  },
);

By using the @Authenticated() decorator, you can protect routes and ensure that only authenticated users can access them.

Conclusion

Custom decorators are a powerful and versatile feature in NestJS that allows you to extend and customize your application’s behavior. With a better understanding of how custom decorators work, you can simplify your code, make it more reusable, and improve its readability.

In this article, we’ve walked through the process of creating and using a custom decorator for tracking borrowed dates in a library application. We’ve also explored the advantages of custom decorators and provided practical examples of their use in logging, validation, and authentication.

As you continue your journey with NestJS, don’t hesitate to explore the possibilities that custom decorators offer. They can be a valuable tool in your toolkit, simplifying your code and enhancing the functionality of your applications. Happy coding!


get in touch to discuss your project
Ashir Afzal

Ashir Afzal

Associate Software Engineer

Ashir Afzal is working as an Associate Software Engineer at Akvateq. He is a full-stack developer, in core PHP, Laravel, and nest js. He is graduated from Iqra University.

get in touch

Akvateq provides you with top performing extended team for all your development needs in any technology. Let us know how we can help you.