DEV Community

Munisekhar Udavalapati
Munisekhar Udavalapati

Posted on

Mastering Decorators in NestJS: A Comprehensive Guide

What Are Decorators?

Decorators are functions that add metadata to a class, method, or property at runtime. In NestJS, they play a crucial role in defining routes, injecting dependencies, applying guards, and more.

Built-in Decorators in NestJS
NestJS provides several built-in decorators to simplify application development:

1. Routing Decorators

@Controller(): Defines a controller for handling requests.
@Get(), @post(), @Put(), @Delete(): Define route handlers for HTTP methods.
Example:

Copy code
@Controller('users')
export class UserController {
  @Get()
  findAll() {
    return 'This action returns all users';
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. Request Object Decorators @body(): Access the request body. @Query(): Access query parameters. @param(): Access route parameters. Example:
Copy code
@Post()
createUser(@Body() createUserDto: CreateUserDto) {
  return `User ${createUserDto.name} created!`;
}
3. Dependency Injection Decorators
@Injectable(): Marks a class as a provider for DI.
@Inject(): Explicitly inject a specific dependency.
Creating Custom Decorators
For advanced use cases, you can create custom decorators to encapsulate reusable logic. Heres how:

Example: Extracting a Token
Create a custom decorator using createParamDecorator:
typescript
Copy code
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const ExtractToken = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const authHeader = request.headers['authorization'];
    if (authHeader && authHeader.startsWith('Bearer ')) {
      return authHeader.slice(7);
    }
    return null;
  },
);
Enter fullscreen mode Exit fullscreen mode

Use the decorator in your controller:


@Controller('auth')
export class AuthController {
  @Get('profile')
  getProfile(@ExtractToken() token: string) {
    return `Token: ${token}`;
  }
}
Enter fullscreen mode Exit fullscreen mode

Why Use Custom Decorators?
Custom decorators improve code reusability, readability, and maintainability. Instead of repeating logic across controllers, encapsulate it in a decorator for consistent and cleaner code.

Tips for Using Decorators in NestJS
Understand Execution Contexts: Decorators like createParamDecorator provide access to request objects, making them ideal for extracting and transforming data.
Leverage Metadata: Use libraries like class-validator to define validation decorators.
Combine Decorators: Use multiple decorators for complex functionalities, such as guards, interceptors, and filters.
Conclusion
Decorators in NestJS offer powerful tools for building clean, scalable applications. Whether you're using built-in decorators or creating custom ones, mastering them will take your NestJS skills to the next level.

Learn More
Official NestJS Docs: Custom Decorators
OpenAPI Integration: Swagger Decorators

import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';

// DTO for creating or updating users
class CreateUserDto {
  name: string;
  email: string;
}

@Controller('users') // Base route: '/users'
export class UserController {
  private users = []; // Example in-memory users array for demonstration

  // GET /users
  @Get()
  findAll() {
    return this.users;
  }

  // GET /users/:id
  @Get(':id')
  findOne(@Param('id') id: string) {
    const user = this.users.find(user => user.id === id);
    return user || { message: `User with ID ${id} not found` };
  }

  // POST /users
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    const newUser = { id: `${Date.now()}`, ...createUserDto };
    this.users.push(newUser);
    return { message: 'User created', user: newUser };
  }

  // PUT /users/:id
  @Put(':id')
  update(@Param('id') id: string, @Body() updateUserDto: CreateUserDto) {
    const userIndex = this.users.findIndex(user => user.id === id);
    if (userIndex === -1) {
      return { message: `User with ID ${id} not found` };
    }
    this.users[userIndex] = { id, ...updateUserDto };
    return { message: 'User updated', user: this.users[userIndex] };
  }

  // DELETE /users/:id
  @Delete(':id')
  delete(@Param('id') id: string) {
    const userIndex = this.users.findIndex(user => user.id === id);
    if (userIndex === -1) {
      return { message: `User with ID ${id} not found` };
    }
    const deletedUser = this.users.splice(userIndex, 1);
    return { message: 'User deleted', user: deletedUser };
  }
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)