banner
cos

cos

愿热情永存,愿热爱不灭,愿生活无憾
github
tg_channel
bilibili

Analysis of Excellent Projects and Best Practices in NestJS Learning

Introduction#

Entering the world of NestJS can be overwhelming, especially when faced with numerous modules and concepts. This article will not only delve into excellent NestJS projects and introduce commonly used built-in Nest modules but also unlock some advanced features and best practices of NestJS to help you better understand and apply this powerful Node.js framework. Whether you are a beginner or an experienced developer, this article will provide you with valuable insights and practical tips to enhance your understanding of NestJS.

I will select outstanding projects from the Awesome NestJS list on GitHub for analysis, examining how they utilize various features and modules of NestJS. Additionally, I will detail some commonly used built-in Nest modules, such as @nestjs/core, and demonstrate their application scenarios through code examples.

This article also includes explanations of some important backend terminology to help you gain a more comprehensive understanding of the framework.

This article will not directly tell you how to build a NestJS project from scratch, as there are already many tutorials and articles available online. Instead, the main purpose of this article is to provide you with a deeper analysis and interpretation, enabling you to be more adept in practical applications. When you encounter a module or feature and are unsure how to use it, this article will serve as a reference and source of inspiration.

Through this perspective and approach, I hope to help you not just "know how to use" but "understand how to use" NestJS, allowing you to engage in backend development with greater confidence and efficiency.

Let’s dive into the world of NestJS together and explore its infinite possibilities!

Overall Architecture & Terminology#

Before diving into the various modules and features of NestJS, it is essential to understand the overall architecture and related terminology of commonly seen excellent projects. This not only helps you better understand how the framework works but also enables you to make more informed decisions during the development process.

Directory Structure#

prisam // Database related
src
├─ auth // Authorization login module
   ├─ auth.controller.ts
   ├─ auth.guard.ts // Guard 
   ├─ auth.interface.ts // Contains local type declarations for this module
   ├─ auth.module.ts
   ├─ auth.service.ts
   ├─ dto
   │   ├─ sign-in.dto.ts
   ├─ entities
   │   └─ refresh-token.entity.ts
├─ common // Global common modules
|   ├─ configs // Global configuration 
|   ├─ constants // Defines some constants
|   ├─ decorators // Global decorators
|   ├─ filters // Global filters
|   ├─ interceptors // Global interceptors
|   ├─ interfaces // Global type declarations
|   ├─ services // Global common services
|   ├─ * // Others
├─ utils // Utility functions, preferably pure functions
├─ app.*.ts // App module, other modules need to reference the app module
├─ main.ts // Application entry

Taking a user authorization module as an example, you typically see these files, and their purposes are as follows:

  • *.module.ts: Typically the module file, used to organize and manage controllers, services, guards, etc. It is the basic unit of a Nest.js application.
  • *.service.ts: The service layer is usually responsible for handling the business logic of the module. They are typically injected into controllers and can access databases, perform calculations, etc.
  • *.controller.ts: The controller file is used to handle HTTP requests and responses. They usually depend on the service to execute business logic.
  • *.guard.ts: The guard file is used to implement route protection, such as authentication and authorization.
  • *.interface.ts: The interface file is used to define local types and data structures to ensure code robustness (e.g., TypeScript declarations).
  • *.dto.ts: Data Transfer Objects (DTOs) are used to validate data sent from the client.
  • *.entity.ts: The entity file is used to define database models.

Here are simple explanations of some terms:

  • DTO (Data Transfer Object): A data transfer object used to transfer data between objects and APIs.
  • Guard: A guard used to implement permission control and access validation.
  • Module: The basic organizational unit of NestJS, used to organize and manage controllers, services, etc.
  • Service: A service that contains the main business logic, usually injected into controllers.
  • Entity: An entity used to define database models, typically used with ORM (Object-Relational Mapping).
  • Interceptor: An interceptor in NestJS is a class annotated with the @Injectable() decorator and implements the NestInterceptor interface. Interceptors are used to perform operations before or after function execution, such as logging, exception handling, data transformation, etc.
  • Reflector: The Reflector is primarily used for reflecting and manipulating metadata. In interceptors, the Reflector can be used to retrieve custom metadata set on methods or classes, allowing for more flexible operations.

Through the above directory structure and terminology explanations, I hope to provide you with a clear perspective to understand the architecture and design philosophy of NestJS more comprehensively. Next, we will delve into these concepts and demonstrate how they are applied in NestJS projects through actual code examples.

Module#

  1. Root Module: Every Nest.js application has a root module, which is the starting point for Nest to build the application graph (application graph). This graph is used to resolve the relationships and dependencies between modules and providers.
  2. Organizing Components: Modules are an effective way to organize and manage components (such as controllers, services, etc.). Through modules, you can group closely related functionalities together.
  3. Multi-module Architecture: For large applications, a multi-module architecture is often adopted. Each module encapsulates a specific set of closely related functionalities.
// Import Nest.js core module
import { Module } from '@nestjs/common';
// Import other related components
import { AppController } from './app.controller';
import { AppService } from './app.service';

// Define the module using the @Module decorator
@Module({
  // Import other modules
  imports: [],
  // Declare the controllers of this module
  controllers: [AppController],
  // Declare the providers of this module (usually services)
  providers: [AppService],
})
export class AppModule {}
  1. Modules | NestJS - A progressive Node.js framework
  2. In-depth Understanding of Nest's Module - Juejin

Service Layer#

In software architecture, there are usually several different layers to organize code and functionality. These layers help achieve separation of concerns, making the code easier to maintain and extend. In this case, we mainly focus on the following layers: Service layer and Controller layer.

Whether it's Nest or Egg, the official demos do not explicitly mention the DAO layer, directly operating the database in the service layer. This is fine for simple business logic, but if the business logic becomes complex, maintaining the service layer will become very difficult. Business logic usually starts simple and will evolve towards complexity; thus, from a long-term perspective, the DAO layer should be retained from the beginning.

The Service layer is primarily responsible for implementing business logic. This layer typically interacts with the database, performs CRUD (Create, Read, Update, Delete) operations, and executes other business logic-related tasks.

For example, a service named UserService might have a registerUser method that accepts a LoginUserDto object, validates the data, and adds the new user to the database.

import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { LoginUserDto } from './dto/LoginUserDto';

@Injectable()
export class AuthService {
  private prisma: PrismaClient;

  constructor() {
    this.prisma = new PrismaClient();
  }

  async registerUser(dto: LoginUserDto): Promise<void> {
    await this.prisma.user.create({
      data: {
        userName: dto.userName,
        password: dto.password,
      },
    });
  }
}
  1. NestJS - Services
  2. Nest Backend Development Practice (Part 2) - Zhihu
  3. Discussion on NestJS Design Philosophy (Layered, IOC, AOP) - Juejin

Controller Layer#

The Controller layer is primarily responsible for handling requests from clients and sending responses. Controllers use methods provided by the Service layer to execute business logic and return results to clients.

For example, a controller named UserController might have a register method that receives an HTTP POST request and LoginUserDto data from the client.

import { Controller, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { LoginUserDto } from './dto/LoginUserDto';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post('register')
  async register(@Body() userDto: LoginUserDto) {
    return await this.userService.registerUser(userDto);
  }
}
  1. Controllers | NestJS
  2. Nest.js - Basic Usage of Controller - Juejin

DTO (Data Transfer Object)#

Using PO and DTO to describe entities and their surroundings. PO corresponds to the persistent object and the database table structure; DTOs are flexible and can describe input parameters or return values in rich scenarios. Below is an example of a user entity:

Relationship with Service Layer and Controller Layer#

  • In the Controller layer, DTOs are used to validate request data from clients. When a client sends a request, Nest.js uses DTOs to validate whether the data in the request body meets the expected format and type.
  • In the Service layer, DTOs are used to execute business logic.

Thus, DTOs become a bridge between the Controller layer and the Service layer, allowing data to flow and transform between these two layers while ensuring type safety and data validation.

In this example, LoginUserDto is a DTO that defines the data format required for user registration. This DTO is used in the Controller layer to receive data from the client and in the Service layer to execute business logic.

// module/dto/LoginUserDto.ts 
import { IsString, IsNotEmpty } from 'class-validator';

export class LoginUserDto {
  @IsString()
  @IsNotEmpty()
  userName: string;

  @IsString()
  @IsNotEmpty()
  password: string;
}

The above code defines the data format required for user registration. It uses the class-validator library for data validation.

  • Property Explanation

    • userName: Username, must be of string type.
    • password: Password, must be of string type.
  • Decorator Explanation

    • @IsString(): Ensures the field is of string type.
    • @IsOptional(): Indicates the field is optional.

For more detailed usage, see https://github.com/typestack/class-validator#usage

  1. Learning Nest.js (Part 5): Using Pipes and DTOs to Validate Input Parameters - Zhihu
  2. NestJS Official Documentation: DTOs and Validation

Entity#

In NestJS or other TypeScript frameworks, .entity.ts files are used to define database models. These models typically correspond to database tables and describe the structure and relationships of the tables. Entity classes usually use decorators to annotate fields and their types so that ORM tools can interact correctly with the database.

For example, an entity named UserEntity might look like this:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity('users')
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 500 })
  name: string;

  @Column('text')
  description: string;
}

In this example, the UserEntity class corresponds to the users table in the database. It has three fields: id, name, and description, and the types and lengths of these fields are annotated using decorators.

If using Prisma ORM, the entity file is generally a pure type definition like:

export class GameInfo {
  id: number;
  name: string | null;
  description: string | null;
}

Because Prisma's definitions are in schema.prisma.

Example Code#

Below is an example of an entity class named RefreshTokenEntity, which is used to store the user's wallet address and JWT access token.

import { Entity, Column, PrimaryColumn } from 'typeorm';
import { IsString } from 'class-validator';
import { Transform } from 'class-transformer';

@Entity('refresh_tokens')
export class RefreshTokenEntity {
  /**
   * User wallet address
   */
  @PrimaryColumn()
  @IsString()
  @Transform(({ value }) => getAddress(value))
  public address: string;

  /**
   * Jwt Token
   */
  @Column('text')
  public accessToken: string;
}

In this example, the RefreshTokenEntity class corresponds to the refresh_tokens table in the database. It has two fields: address and accessToken. The address field also uses decorators from the class-validator and class-transformer libraries for additional validation and transformation.

Thus, you can use this entity class for database operations in the Service layer or DAO layer.

  1. TypeORM - Entity
  2. NestJS - TypeORM

Guard#

In NestJS and some other backend frameworks, Guards are a special type of service used to implement route protection. They are typically used for authentication and authorization to ensure that only users with appropriate permissions can access specific routes or perform specific actions.

For example, below is a simple guard named AuthGuard, which uses JWT (JSON Web Token) for authentication:

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Reflector } from '@nestjs/core';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private jwtService: JwtService,
    private configService: ConfigService,
    private reflector: Reflector
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = request.headers['authorization']?.split(' ')[1];

    if (!token) {
      throw new UnauthorizedException('Token is missing');
    }

    try {
      const decoded = this.jwtService.verify(token);
      request.user = decoded;
      return true;
    } catch (error) {
      throw new UnauthorizedException('Invalid token');
    }
  }
}

In this example, AuthGuard implements the CanActivate interface and defines a canActivate method. This method checks whether a valid JWT is present in the request header. If it is, the request is allowed to proceed; otherwise, an UnauthorizedException is thrown.

  1. NestJS - Guards
  2. Detailed Explanation of NestJS Guards - Juejin
  3. NestJS Practical: Using Guards for Permission Control - Zhihu

By using the Service layer and Guard layer, you can organize your code more effectively, making it more modular and maintainable. This also helps achieve more robust and flexible business logic and security control.

Interceptor#

In NestJS, an Interceptor is a class annotated with the @Injectable() decorator and implements the NestInterceptor interface. Interceptors are typically used to perform operations before or after function execution. They can be used for various purposes, such as logging, exception handling, data transformation, etc.

A simple LoggingInterceptor:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');

    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`)),
      );
  }
} 

Since handle() returns an RxJS Observable, we can use various operators to manipulate the stream. In the above example, we used the tap() operator, which calls our anonymous logging function when the observable stream terminates normally or with an error, but does not otherwise interfere with the response cycle.

Response Data Transformation Interceptor#

A simple example of a response data transformation interceptor is shown below, which can return either raw data or wrapped data as needed.

Define Response Interface and Metadata Key#

First, we define a response interface IResponse and a metadata key IS_RAW_DATA_KEY.

import { SetMetadata } from '@nestjs/common';
  
export interface IResponse<T> {
  code: number;
  message: string;
  data: T;
}

export const IS_RAW_DATA_KEY = 'is-raw-data';

/**
 * Control whether to wrap the response data
 * @constructor
 */
export const RawData = () => SetMetadata(IS_RAW_DATA_KEY, true);
  • IResponse<T>: An interface for wrapping response data.
  • IS_RAW_DATA_KEY: A metadata key used to mark whether to return raw data.
  • RawData(): A decorator used to set the metadata key.

Don’t understand metadata? No worries, let’s move on.

Implementing the Interceptor#

Next, we implement the interceptor for transforming response data.

// transform.interceptor.ts
import { IResponse, IS_RAW_DATA_KEY } from '@/common';
import { map, Observable } from 'rxjs';
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

/**
 * Handle response data transformation
 */
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, IResponse<T> | T> {
  constructor(private reflector: Reflector) {}

  intercept(context: ExecutionContext, next: CallHandler<T>): Observable<IResponse<T> | T> {
    const isRawData = this.reflector.getAllAndOverride<boolean>(IS_RAW_DATA_KEY, [context.getHandler(), context.getClass()]);
    return next.handle().pipe(map((data) => (isRawData ? data : { code: 200, message: 'success', data })));
  }
}
  • TransformInterceptor: The main body of the interceptor.
  • isRawData: Used to check whether to return raw data.
  • next.handle().pipe(...): Decides whether to return raw data or wrapped data based on the value of isRawData.
Referencing the Interceptor in AppModule#

Finally, add the interceptor in the AppModule.

import { TransformInterceptor } from '@/common';
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    // others...
  ],
  controllers: [AppController],
  providers: [
    // others...
    {
      provide: APP_INTERCEPTOR,
      useClass: TransformInterceptor,
    },
    AppService,
  ],
})
export class AppModule {}
Using the RawData Decorator#

Now, you can use the @RawData() decorator in the Controller to control whether to return raw response data.

@Controller('someResource')
export class SomeController {
  constructor(private someService: SomeService) {}

  @RawData()
  @Get(':someParam')
  someMethod(@Param('someParam') someParam: number): Promise<SomeEntity> {
    // Implementation details
  }
}

In this way, you can flexibly control the format of the response data.

Nest.js is quite magical, isn't it?

  1. NestJS Official Documentation: Interceptors
  2. Guide and Use Cases for NestJS Interceptors - LogRocket Blog

This way, you can better understand the role and implementation of the Interceptor layer in NestJS. I hope this helps you gain a deeper understanding of the architecture and best practices of NestJS.

Reflector#

In NestJS, the Reflector is a utility class used to retrieve metadata. It is often used in custom decorators and interceptors to obtain additional information about classes, methods, or properties. This information may be added at compile time through decorators.

In the above interceptor example, the Reflector is used to get metadata related to the current execution context (ExecutionContext). Here, it is used to check whether to return raw data or whether to wrap the data in a response object.

const isRawData = this.reflector.getAllAndOverride<boolean>(IS_RAW_DATA_KEY, [context.getHandler(), context.getClass()]);

This line of code uses the getAllAndOverride method to retrieve the metadata for IS_RAW_DATA_KEY from the current handler or class. This metadata is then used to decide how to transform the response data.

Use Cases#

  1. Custom Decorators: If you have a custom decorator, you may need to use the Reflector to read the metadata associated with that decorator.
  2. Permission Control: In interceptors or guards, you can use the Reflector to obtain metadata about user roles or permissions for more granular control.
  3. Response Transformation: As shown in the above example, you can use the Reflector to decide how to format or transform output data.

Example Code#

import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    // ... Permission check logic
  }
}
  1. NestJS - Reflector
  2. NestJS - Custom Decorators
  3. In-depth Understanding of Reflector's Use Cases in NestJS

Thus, the Reflector becomes a key tool in NestJS for implementing highly configurable and dynamic behaviors.

Common Built-in Nest Modules#

This is a summary of module introductions derived from various Nest projects' package.json files:

@nestjs/core#

  • NPM: @nestjs/core
  • Documentation: NestJS Core Module
  • Introduction: This is the core module of the NestJS framework, providing the basic building blocks and core functionalities of the framework.
  • Use Cases: Used to build and initialize NestJS applications; almost all NestJS projects will use it.
  • Code Example:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
### @nestjs/jwt
- NPM: [@nestjs/jwt](https://www.npmjs.com/package/@nestjs/jwt)
- Documentation: [NestJS JWT Module](https://docs.nestjs.com/security/authentication#jwt-module)
- **Introduction**: This module provides support for JWT (JSON Web Tokens) to implement **authentication and authorization** in NestJS applications.
- **Use Cases**: Authentication and authorization, typically used to protect routes and resources.
- **Code Example**:
```ts
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  async generateToken(user: User) {
    return this.jwtService.sign({ user });
  }
}

@nestjs/config#

  • NPM: @nestjs/config
  • Documentation: NestJS Config Module
  • Introduction: This module is used to manage configuration information for NestJS applications. It supports environment variables, type conversion, etc.
  • Use Cases: Used to manage application configuration information, such as database connection strings, API keys, etc.
  • Code Example:
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AppService {
  constructor(private configService: ConfigService) {}

  getDatabaseUrl(): string {
    return this.configService.get<string>('DATABASE_URL');
  }
}

@nestjs/common#

  • NPM: @nestjs/common
  • Documentation: NestJS Common Module
  • Introduction: This is a common module of NestJS that provides a series of commonly used decorators, helper functions, and other tools, such as Injectable, Module, BadRequestException, Body, Controller, Get, Param, Post, Query, etc.
  • Use Cases: Used in the Controller layer, Service layer, and Module to define routes, dependency injection, etc.
  • Code Example:
import { Controller, Get } from '@nestjs/common';

@Controller('hello')
export class HelloController {
  @Get()
  sayHello(): string {
    return 'Hello World!';
  }
}

@nestjs/axios#

  • NPM: @nestjs/axios
  • Documentation: NestJS Axios Module
  • Introduction: This module provides a wrapper for the Axios HTTP client, making it easier to make HTTP requests in NestJS applications. Such as HttpService, HttpModule.
  • Use Cases: Making HTTP requests in the Service layer, such as calling third-party APIs.
  • Code Example:
import { HttpService } from '@nestjs/axios';

@Injectable()
export class ApiService {
  constructor(private httpService: HttpService) {}

  async fetchData(url: string) {
    const response = await this.httpService.get(url).toPromise();
    return response.data;
  }
}

In actual projects, it is common to rewrite the HTTP Service request to add unified logging, as shown below:

import { HttpService } from '@nestjs/axios';
import { catchError, Observable, tap } from 'rxjs';
import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { BadRequestException, Injectable, Logger } from '@nestjs/common';

@Injectable()
export class HttpClientService {
  constructor(private httpService: HttpService) {}

  private logger: Logger = new Logger(HttpClientService.name);
  
  /**
   * Rewrite HTTP Service GET request, print Request and Response
   * @param url
   * @param config
   */
  get<T = any>(url: string, config?: AxiosRequestConfig): Observable<AxiosResponse<T, any>> {
    // Print request information
    this.logger.log(`GET ${url}`);
    return this.httpService.get<T>(url, config).pipe(
      // Print received response without changing the Observable stream
      tap((response) => this.logger.log(`Response ${url} ${JSON.stringify(response.data)}`)),
      // Catch errors and print error information
      catchError((error: AxiosError) => {
        const errorData = JSON.stringify(error.response?.data);
        this.logger.error(`GET Error ${url} ${errorData}`);
        throw new BadRequestException([errorData]);
      }),
    );
  }
}

@nestjs/bull#

  • NPM: @nestjs/bull
  • Documentation: NestJS Bull Module
  • Introduction: This module provides a wrapper for the Bull queue library, used to handle background jobs and message queues in NestJS applications.
  • Use Cases: Background task processing, such as sending emails, data processing, etc.
  • Code Example:
import { Processor, Process } from '@nestjs/bull';
import { Job } from 'bull';

@Processor('audio')
export class AudioProcessor {
  @Process('transcode')
  async transcode(job: Job<number>) {
    // Your logic here
  }
}

@nestjs/cache-manager#

  • NPM: @nestjs/cache-manager
  • Documentation: NestJS Caching
  • Introduction: This module provides caching management functionality, supporting various caching storage methods, such as memory, Redis, etc.
  • Use Cases: Data caching, such as API responses, database query results, session caching, etc.
  • Code Example: In this example, we use CacheModule to register caching and use CacheInterceptor to automatically handle caching logic. This way, when accessing the findAll method multiple times, the results will be cached, improving response speed.
import { CacheModule, CacheInterceptor, Controller, UseInterceptors } from '@nestjs/common';
import { CachingConfigService } from './caching-config.service';

@Module({
  imports: [
    CacheModule.registerAsync({
      useClass: CachingConfigService,
    }),
  ],
})
export class AppModule {}

@Controller('posts')
export class PostsController {
  @UseInterceptors(CacheInterceptor)
  @Get()
  findAll() {
    // Your logic here
  }
}

@nestjs/mongoose#

  • NPM: @nestjs/mongoose
  • Documentation: NestJS Mongoose Module
  • Introduction: This module provides a wrapper for Mongoose ODM (Object Document Mapping) to interact with MongoDB databases in NestJS applications.
  • Use Cases: Database operations, especially interactions with MongoDB databases.
  • Code Example:
import { Schema, Prop, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

@Schema()
export class Cat extends Document {
  @Prop()
  name: string;
}

export const CatSchema = SchemaFactory.createForClass(Cat);

@nestjs/platform-express#

  • NPM: @nestjs/platform-express
  • Documentation: NestJS Overview
  • Introduction: This module is the Express adapter for the NestJS framework, used to utilize Express.js in NestJS applications.
  • Use Cases: Used for file uploads with FileInterceptor.
  • Code Example: In this example, we use the FileInterceptor provided by @nestjs/platform-express to handle file uploads. This is essentially a wrapper around Express's multer middleware.
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';

@Controller('upload')
export class UploadController {
  @Post()
  @UseInterceptors(FileInterceptor('file', {
    storage: diskStorage({
      destination: './uploads',
      filename: (req, file, cb) => {
        cb(null, `${Date.now()}-${file.originalname}`);
      },
    }),
  }))
  uploadFile(@UploadedFile() file) {
    return { url: `./uploads/${file.filename}` };
  }
}

@nestjs/schedule#

  • NPM: @nestjs/schedule
  • Documentation: NestJS Schedule Module
  • Introduction: This module provides task scheduling functionality to execute scheduled tasks in NestJS applications.
  • Use Cases: Scheduled tasks, such as daily data backups, timed pushes, etc.
  • Code Example:
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  @Cron(CronExpression.EVERY_5_SECONDS)
  handleCron() {
    // Your logic here
  }
}

@nestjs/swagger#

  • NPM: @nestjs/swagger
  • Documentation: NestJS Swagger Module
  • Introduction: This module is used to generate and maintain API documentation based on Swagger.
  • Use Cases: Automatically generating API documentation.
  • Code Example:
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
  @ApiProperty()
  username: string;

  @ApiProperty()
  password: string;
}

@nestjs/throttler#

  • NPM: @nestjs/throttler
  • Documentation: NestJS Throttler
  • Introduction: This module provides request limiting functionality to prevent API abuse. It includes ThrottlerGuard, SkipThrottle, etc.
  • Use Cases: Preventing API abuse, such as limiting the number of requests per minute.
  • Code Example:
import { ThrottlerGuard } from '@nestjs/throttler';

@Injectable()
export class AppGuard extends ThrottlerGuard {
  // Your logic here
}

Other Packages#

class-validator#

import { IsEmail, IsNotEmpty } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty()
  name: string;

  @IsEmail()
  email: string;
}

class-transformer#

  • NPM: class-transformer
  • Documentation: class-transformer GitHub
  • Introduction: A library for transforming between objects and class instances, usually used in conjunction with class-validator.
  • Use Cases: Object transformation and serialization.
  • Code Example:
import { plainToClass } from 'class-transformer';

const user = plainToClass(User, {
  name: 'John Doe',
  email: '[email protected]'
});

cache-manager#

  • NPM: cache-manager
  • Documentation: cache-manager GitHub
  • Introduction: A flexible and extensible caching module that supports various storage methods.
  • Use Cases: Data caching, such as API responses, database query results, etc.
  • Code Example:
import * as cacheManager from 'cache-manager';

const memoryCache = cacheManager.caching({ store: 'memory', max: 100, ttl: 10 });

async function getUser(id: string) {
  const cachedUser = await memoryCache.get(id);
  if (cachedUser) {
    return cachedUser;
  }

  const user = await fetchUserFromDb(id);
  await memoryCache.set(id, user);
  return user;
}

hashids#

import Hashids from 'hashids';

const hashids = new Hashids();
const id = hashids.encode(12345); // Outputs a short unique string

ioredis#

  • NPM: ioredis
  • Documentation: ioredis GitHub
  • Introduction: A robust and efficient Redis client.
  • Use Cases: Redis database operations, caching management, etc.
  • Code Example:
import Redis from 'ioredis';

const redis = new Redis();
redis.set('key', 'value');

mongoose#

  • NPM: mongoose
  • Documentation: Mongoose Documentation
  • Introduction: An Object Data Modeling (ODM) library for MongoDB and Node.js.
  • Use Cases: MongoDB database operations, data model definitions, etc.
  • Code Example:
import mongoose from 'mongoose';

const userSchema = new mongoose.Schema({
  username: String,
  password: String,
});

const User = mongoose.model('User', userSchema);

nestgram#

  • NPM: nestgram
  • Documentation: About Nestgram - Nestgram
  • Introduction: A library for integrating Telegram bots in NestJS.
  • Use Cases: Integrating Telegram bots in NestJS applications.
  • Code Example:
import { Nestgram } from 'nestgram';

const nestgram = new Nestgram({ token: 'YOUR_BOT_TOKEN' });
nestgram.on('message', (msg) => {
  // Handle message
});

nestjs-throttler-storage-redis#

import { ThrottlerModule } from '@nestjs/throttler';
import { RedisThrottlerStorage } from 'nestjs-throttler-storage-redis';

@Module({
  imports: [
    ThrottlerModule.forRoot({
      storage: new RedisThrottlerStorage(),
    }),
  ],
})
export class AppModule {}

ramda#

  • NPM: ramda
  • Documentation: Ramda Documentation
  • Introduction: A practical functional programming library.
  • Use Cases: Data transformation, function composition, etc.
  • Code Example:
import * as R from 'ramda';

const addOne = R.add(1);
const result = addOne(2); // Outputs 3

redis#

  • NPM: redis
  • Documentation: redis GitHub
  • Introduction: A Redis client for Node.js.
  • Use Cases: Interacting with Redis databases in Node.js applications.
  • Code Example:
import * as redis from 'redis';
const client = redis.createClient();

client.set('key', 'value');
client.get('key', (err, reply) => {
  console.log(reply); // Outputs 'value'
});

reflect-metadata#

import 'reflect-metadata';

@Reflect.metadata('role', 'admin')
class User {
  constructor(public name: string) {}
}

const metadata = Reflect.getMetadata('role', User);
console.log(metadata); // Outputs 'admin'

rxjs#

  • NPM: rxjs
  • Documentation: RxJS Documentation
  • Introduction: A library for reactive programming using observables.
  • Use Cases: Asynchronous programming, event handling, etc.
  • Code Example:
import { of } from 'rxjs';
import { map } from 'rxjs/operators';

const source$ = of(1, 2, 3);
const result$ = source$.pipe(map(x => x * 2));

result$.subscribe(x => console.log(x)); // Outputs 2, 4, 6
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.