NestJS Study: Excellent Project Analysis and Best Practices

发表于 2023-09-23 23:28 4764 字 24 min read

cos avatar

cos

FE / ACG / 手工 / 深色模式强迫症 / INFP / 兴趣广泛养两只猫的老宅女 / remote

本文深入解析了 NestJS 框架的核心架构与设计思想,通过分析优秀项目的目录结构和模块化实践,介绍了模块、服务、控制器、DTO、实体、守卫、拦截器和反射器等关键概念及其在实际开发中的应用。文章结合代码示例,详细讲解了各组件之间的协作机制,以及如何通过 DTO 实现数据验证、通过守卫和拦截器实现权限控制与日志记录,帮助开发者真正“懂得用”NestJS,提升后端开发的可维护性与灵活性。

This article has been machine-translated from Chinese. The translation may contain inaccuracies or awkward phrasing. If in doubt, please refer to the original Chinese version.

Preface

Entering the world of NestJS can feel overwhelming, especially when you face numerous modules and concepts. This article will not only deeply analyze excellent NestJS projects and introduce commonly used Nest built-in modules, but will 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 valuable insights and practical tips to help you get to know NestJS better.

I will select outstanding projects from the GitHub Awesome NestJS list for analysis, examining how they use various NestJS features and modules. Additionally, I will provide detailed introductions to some commonly used Nest built-in modules like @nestjs/core, with code examples showing their application scenarios.

This article also includes explanations of some important backend terminology to help you more comprehensively understand the framework.

This article will not tell you how to build a NestJS project from scratch, as there are already many such tutorials and articles online. Instead, the main purpose of this article is to help you become more proficient in practical applications through in-depth analysis and interpretation. When you encounter a module or feature you don’t know how to use, this article will serve as your reference and source of inspiration.

Through this perspective and approach, I hope to help you not just “know how to use” but truly “understand how to use” NestJS, making you more confident and efficient in backend development.

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

Overall Architecture & Terminology

Before diving into NestJS’s various modules and features, understanding the overall architecture and related terminology of common excellent projects is very important. This not only helps you better understand how the framework works but also enables you to make wiser decisions during development.

Directory Structure

prisam // Database related
src
├─ auth // Authorization and login module
   ├─ auth.controller.ts
   ├─ auth.guard.ts // Guard
   ├─ auth.interface.ts // 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 module
|   ├─ configs // Global configurations
|   ├─ constants // Define 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 point

Taking a user authorization module as an example, you typically see these files with the following purposes:

  • *.module.ts: Usually the module file, used to organize and manage controllers, services, guards, etc. It is the fundamental unit of a Nest.js application.
  • *.service.ts: The Service layer typically handles the module’s business logic. They are usually injected into controllers and can access databases, perform computations, etc.
  • *.controller.ts: Controller files handle HTTP requests and responses. They typically depend on Services to execute business logic.
  • *.guard.ts: Guard files implement route protection, such as authentication and authorization.
  • *.interface.ts: Interface files define locally used types and data structures to ensure code robustness (TypeScript declarations, etc.).
  • *.dto.ts: Data Transfer Objects (DTOs) validate data sent by clients.
  • *.entity.ts: Entity files define database models.

Brief explanations of some terms:

  • DTO (Data Transfer Object): Used to transfer data between objects and APIs.
  • Guard: Implements permission control and access verification.
  • Module: The basic organizational unit of NestJS, used to organize and manage controllers, services, etc.
  • Service: Contains the main business logic, typically injected into controllers.
  • Entity: Defines database models, typically used with ORM (Object-Relational Mapping).
  • Interceptor: A class annotated with the @Injectable() decorator in NestJS that implements the NestInterceptor interface. Interceptors are used to perform operations before or after function execution, such as logging, exception handling, data transformation, etc.
  • Reflector: Primarily used for metadata reflection and manipulation. In interceptors, Reflector can be used to retrieve custom metadata set on methods or classes, enabling more flexible operations.

Through the directory structure and terminology above, I hope to provide you with a clear perspective for a more comprehensive understanding of NestJS’s architecture and design philosophy. Next, we will explore these concepts in depth 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 Nest uses to build the application graph. This graph is used to resolve 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 functionality together.
  3. Multi-Module Architecture: For large applications, a multi-module architecture is typically adopted. Each module encapsulates a specific set of closely related functionality.
// Import Nest.js core module
import { Module } from '@nestjs/common';
// Import other related components
import { AppController } from './app.controller';
import { AppService } from './app.service';

// Use @Module decorator to define the module
@Module({
  // Import other modules
  imports: [],
  // Declare controllers for this module
  controllers: [AppController],
  // Declare providers for this module (usually services)
  providers: [AppService],
})
export class AppModule {}
  1. Modules | NestJS - A progressive Node.js framework
  2. Deep Dive into Nest Modules - Juejin

Service Layer

In software architecture, there are typically several different layers to organize code and functionality. These layers help achieve Separation of Concerns, making code easier to maintain and extend. In this example, we mainly focus on the Service layer and Controller layer. As for the DAO layer:

Whether in Nest or Egg, the official demos don’t explicitly mention a 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 becomes very difficult. Business usually starts simple but inevitably evolves toward complexity, so if you think long-term, you should include the DAO layer from the start.

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

For example, a UserService service might have a registerUser method that accepts a LoginUserDto object, validates the data, and adds a 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 in Practice (Part 2) - Layering - Zhihu
  3. NestJS Design Philosophy (Layering, 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 the client.

For example, a UserController controller might have a register method that accepts 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 - Controller Basic Usage - Juejin

DTO (Data Transfer Object)

Use PO and DTO to describe entities and their peripherals. PO is the persistent object corresponding one-to-one with database table structures; DTO is a flexible data transfer object that can describe parameters or return values in rich scenarios. Here is a user entity example:

Relationship with Service and Controller Layers

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

Thus, DTOs serve as a bridge between the Controller and Service layers, enabling data to flow and transform between the two 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 receives client data in the Controller layer and is used for business logic in the Service layer.

// 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 a string type.
    • password: Password, must be a string type.
  • Decorator Explanation

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

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

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

Entity

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

For example, a UserEntity entity 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, whose types and lengths are also annotated through decorators.

If using Prisma ORM, entity files are generally plain type definitions like:

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

Because Prisma definitions are in the schema.prisma file.

Example Code

The following is a RefreshTokenEntity entity class example that stores a 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 uses additional validation and transformation decorators from the class-validator and class-transformer libraries.

This way, you can use this entity class for database operations in the Service 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, ensuring that only users with appropriate permissions can access specific routes or perform specific operations.

For example, here is a simple AuthGuard that 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 the request header contains a valid JWT. If it does, the request is allowed to proceed; otherwise, an UnauthorizedException is thrown.

  1. NestJS - Guards
  2. NestJS Guards Explained - Juejin
  3. NestJS in Practice: Permission Control with Guards - Zhihu

By using Service and Guard layers, you can organize your code more effectively, making it more modular and maintainable. This also helps implement more powerful and flexible business logic and security controls.

Interceptor

In NestJS, an Interceptor is a class annotated with the @Injectable() decorator that implements the NestInterceptor interface. Interceptors are typically used to perform operations before or after function execution. They can serve multiple 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 example above, we used the tap() operator, which invokes our anonymous logging function when the observable stream terminates normally or abnormally, without otherwise interfering with the response cycle.

Response Data Transformation Interceptor

A simple response data transformation interceptor example that can return 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 returned data is wrapped through Response
 * @constructor
 */
export const RawData = () => SetMetadata(IS_RAW_DATA_KEY, true);
  • IResponse<T>: Interface for wrapping response data.
  • IS_RAW_DATA_KEY: Metadata key for marking whether to return raw data.
  • RawData(): A decorator for setting the metadata key.

Don’t understand metadata? Don’t worry, read on.

Implement the Interceptor

Next, we implement the response data transformation interceptor.

// 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 raw data should be returned.
  • next.handle().pipe(...): Decides whether to return raw data or wrapped data based on isRawData.
Reference the Interceptor in AppModule

Finally, add the interceptor in 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 Controllers 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
  }
}

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

Pretty magical, right? That’s Nest.js.

  1. NestJS Official Documentation: Interceptors
  2. NestJS interceptors: Guide and use cases - LogRocket Blog

This should help you better understand the role and implementation of the Interceptor layer in NestJS. I hope it helps you gain deeper insight into NestJS’s architecture and best practices.

Reflector

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

In the interceptor example above, Reflector was used to retrieve metadata related to the current execution context (ExecutionContext). Here, it checks whether raw data should be returned or whether the data should be wrapped 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 get the IS_RAW_DATA_KEY metadata 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 Reflector to read metadata related to that decorator.
  2. Permission Control: In interceptors or guards, you can use Reflector to get metadata about user roles or permissions for finer-grained control.
  3. Response Transformation: As shown in the example above, you can use Reflector to decide how to format or transform outgoing 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. Deep Understanding of Reflector Use Cases in NestJS

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

Common Nest Built-in Modules

These are module introductions summarized from examining the package.json files of various Nest projects:

@nestjs/core

  • NPM: @nestjs/core
  • Docs: NestJS Core Module
  • Introduction: This is the core module of the NestJS framework, providing the foundational building blocks and core functionality.
  • Use Case: Used to build and initialize NestJS applications; almost all NestJS projects 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
  • Docs: NestJS JWT Module
  • Introduction: This module provides JWT (JSON Web Tokens) support for implementing authentication and authorization in NestJS applications.
  • Use Case: Authentication and authorization, typically used to protect routes and resources.
  • Code Example:
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
  • Docs: NestJS Config Module
  • Introduction: This module is used to manage configuration information for NestJS applications. It supports environment variables, type conversion, etc.
  • Use Case: Managing application configuration 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
  • Docs: NestJS Common Module
  • Introduction: A general-purpose NestJS module providing commonly used decorators, helper functions, and other tools. Such as Injectable, Module, BadRequestException, Body, Controller, Get, Param, Post, Query, etc.
  • Use Case: Used in Controllers, Services, and Modules for defining 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
  • Docs: NestJS Axios Module
  • Introduction: This module provides an Axios HTTP client wrapper for NestJS, making HTTP requests more convenient. Such as HttpService, HttpModule.
  • Use Case: 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 real projects, the HTTP Service is typically customized to add unified logging, etc., like this:

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);

  /**
   * Override 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 info
    this.logger.log(`GET ${url}`);
    return this.httpService.get<T>(url, config).pipe(
      // Print received response without modifying the Observable stream
      tap((response) => this.logger.log(`Response ${url} ${JSON.stringify(response.data)}`)),
      // Catch errors and print error info
      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
  • Docs: NestJS Bull Module
  • Introduction: This module provides a Bull queue library wrapper for handling background jobs and message queues in NestJS applications.
  • Use Case: 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
  • Docs: NestJS Caching
  • Introduction: This module provides cache management functionality supporting multiple cache storage methods such as memory, Redis, etc.
  • Use Case: Data caching, such as API responses, database query results, session caching, etc.
  • Code Example: In this example, we use CacheModule to register the cache and the CacheInterceptor to automatically handle caching logic. This way, when findAll is accessed multiple times, the results are 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
  • Docs: NestJS Mongoose Module
  • Introduction: This module provides a Mongoose ODM (Object Document Mapping) wrapper for interacting with MongoDB databases in NestJS applications.
  • Use Case: Database operations, especially MongoDB interactions.
  • 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
  • Docs: NestJS Overview
  • Introduction: This module is the Express adapter for the NestJS framework, used to integrate Express.js into NestJS applications.
  • Use Case: Uses FileInterceptor for file upload handling.
  • Code Example: In this example, we use the FileInterceptor from @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
  • Docs: NestJS Schedule Module
  • Introduction: This module provides task scheduling functionality for executing scheduled tasks in NestJS applications.
  • Use Case: Scheduled tasks, such as daily data backup, timed notifications, 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
  • Docs: NestJS Swagger Module
  • Introduction: This module is used to generate and maintain API documentation, based on Swagger.
  • Use Case: Automatic API documentation generation.
  • Code Example:
import { ApiProperty } from '@nestjs/swagger';

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

  @ApiProperty()
  password: string;
}

@nestjs/throttler

  • NPM: @nestjs/throttler
  • Docs: NestJS Throttler
  • Introduction: This module provides Rate Limiting functionality to prevent API abuse. Includes ThrottlerGuard, SkipThrottle, etc.
  • Use Case: Preventing API abuse, such as limiting 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
  • Docs: class-transformer GitHub
  • Introduction: Used for transformation between objects and class instances, typically used in conjunction with class-validator.
  • Use Case: Object transformation and serialization.
  • Code Example:
import { plainToClass } from 'class-transformer';

const user = plainToClass(User, {
  name: 'John Doe',
  email: 'john.doe@example.com',
});

cache-manager

  • NPM: cache-manager
  • Docs: cache-manager GitHub
  • Introduction: A flexible, extensible caching module supporting multiple storage methods.
  • Use Case: 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
  • Docs: ioredis GitHub
  • Introduction: A robust, high-performance Redis client.
  • Use Case: Redis database operations, cache management, etc.
  • Code Example:
import Redis from 'ioredis';

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

mongoose

  • NPM: mongoose
  • Docs: Mongoose Documentation
  • Introduction: An Object Data Modeling (ODM) library for MongoDB and Node.js.
  • Use Case: 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
  • Docs: About Nestgram - Nestgram
  • Introduction: A Telegram bot library for NestJS.
  • Use Case: 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
  • Docs: Ramda Documentation
  • Introduction: A practical functional programming library.
  • Use Case: 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
  • Docs: redis GitHub
  • Introduction: A Node.js Redis client.
  • Use Case: 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
  • Docs: RxJS Documentation
  • Introduction: A library for reactive programming using observables.
  • Use Case: 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

喜欢的话,留下你的评论吧~

© 2020 - 2026 cos @cosine
Powered by theme astro-koharu · Inspired by Shoka