新手入门:nest基本使用规则(适合零基础小白)

基础概念

1. 介绍 NestJS

NestJS 是一个用于构建高效、可扩展的 Node.js 服务端应用程序的框架。它基于 TypeScript 开发,结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数式响应式编程)的最佳实践。

为什么选择 NestJS?

  • TypeScript 支持:完整的类型安全,减少运行时错误
  • 模块化架构:清晰的代码组织方式
  • 依赖注入:松耦合的组件设计
  • 丰富的生态:与主流数据库、ORM、认证系统等无缝集成
  • 企业级应用:适合构建大型、复杂的应用程序

一个标准的 NestJS 项目结构如下:

bash 复制代码
project-name/
├── src/
│   ├── main.ts                 # 应用入口文件
│   ├── app.module.ts           # 根模块
│   ├── app.controller.ts       # 控制器
│   ├── app.service.ts          # 服务
│   └── app.controller.spec.ts  # 测试文件
├── test/                       # 测试目录
├── nest-cli.json               # Nest CLI 配置
├── package.json                # 依赖管理
└── tsconfig.json               # TypeScript 配置

核心文件说明:

  • main.ts:应用的入口点,创建 Nest 应用实例
  • app.module.ts:根模块,组织应用的各个部分
  • app.controller.ts:处理 HTTP 请求
  • app.service.ts:包含业务逻辑

2. 核心概念 - 控制器与服务

2.1 控制器(Controllers)

控制器 负责处理客户端请求并返回响应。它们定义了应用的路由和请求处理逻辑。

创建控制器
typescript 复制代码
// src/users/users.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
@Controller('users')
export class UsersController {
  @Get()
  findAll() {
    return 'This action returns all users';
  }
  @Get(':id')
  findOne(id: string) {
    return `This action returns a #${id} user`;
  }
  @Post()
  create(@Body() createUserDto: any) {
    return `This action adds a new user: ${createUserDto.name}`;
  }
}
控制器装饰器
  • @Controller('users'):定义控制器的基本路由
  • @Get():处理 GET 请求
  • @Post():处理 POST 请求
  • @Put():处理 PUT 请求
  • @Delete():处理 DELETE 请求

2.2 服务(Services)

服务 包含应用的业务逻辑,被控制器调用。它们通过依赖注入的方式提供给控制器使用。

创建服务
typescript 复制代码
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
  private users = [
    { id: '1', name: 'John Doe', email: 'john@example.com' },
    { id: '2', name: 'Jane Doe', email: 'jane@example.com' }
  ];
  findAll() {
    return this.users;
  }
  findOne(id: string) {
    return this.users.find(user => user.id === id);
  }
  create(user: any) {
    const newUser = {
      id: Date.now().toString(),
      ...user
    };
    this.users.push(newUser);
    return newUser;
  }
  update(id: string, updateUser: any) {
    const index = this.users.findIndex(user => user.id === id);
    if (index >= 0) {
      this.users[index] = { ...this.users[index], ...updateUser };
      return this.users[index];
    }
    return null;
  }
  remove(id: string) {
    const index = this.users.findIndex(user => user.id === id);
    if (index >= 0) {
      return this.users.splice(index, 1);
    }
    return null;
  }
}

2.3 依赖注入

NestJS 使用依赖注入模式来管理组件之间的依赖关系:

typescript 复制代码
// src/users/users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}
  @Get()
  findAll() {
    return this.usersService.findAll();
  }
  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(id);
  }
  @Post()
  create(@Body() createUserDto: any) {
    return this.usersService.create(createUserDto);
  }
}

3. 模块系统

3.1 模块的概念

模块 是组织应用程序结构的基本单元。每个 Nest 应用程序至少有一个根模块(AppModule)。

3.2 创建功能模块

typescript 复制代码
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}

3.3 根模块

typescript 复制代码
// src/app.module.ts
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
@Module({
  imports: [UsersModule],
})
export class AppModule {}

3.4 模块的属性

  • controllers:定义属于该模块的控制器
  • providers:定义由 Nest 注入器实例化的服务
  • imports:导入其他模块
  • exports:导出服务供其他模块使用
kotlin 复制代码
// 共享模块示例
@Module({
  providers: [SharedService],
  exports: [SharedService]
})
export class SharedModule {}

4. 路由与请求处理

4.1 路由参数

路径参数

如下示例, 框架内置了动态匹配路由的逻辑,假如客户端访问 域名+端口号/images/text-to-image ,且方法是post,会自动走到generateTextToImage并且走相应的service去返回对应的数据

typescript 复制代码
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
import { ImagesService } from './images.service';
import {
  GenerateImageToImageDto,
  GenerateTextTOImageDto,
} from './dto/generate-image.dto';
import { GenerationMode } from '../tasks/entities/generation-task.entity';

@Controller('images')
export class ImagesController {
  constructor(private readonly imagesService: ImagesService) {}

  /**
   * POST images/text-to-image
   * 创建任务并转发给 n8n
   */
  @Post('text-to-image')
  async generateTextToImage(@Body() dto: GenerateTextTOImageDto) {
    return this.imagesService.createTextToImage(dto);
  }

  /**
   * POST images/image-to-image
   * 创建任务并转发给 n8n
   */
  @Post('image-to-image')
  async generateImageToImage(@Body() dto: GenerateImageToImageDto) {
    return this.imagesService.createImageToImage(dto);
  }

  /**
   * GET /images/:id
   * 便于前端轮询查询(或调试)
   */
  @Get(':id')
  async get(@Param('id') id: string) {
    return this.imagesService.getTask(id);
  }

  /**
   * 查询多角度任务聚合状态
   */ @Get('multi-angle/:bizId') async getMultiAngleTasks(
    @Param('bizId') bizId: string,
  ): Promise<
    Array<{
      id: string;
      prompt: string;
      status: string;
      angle?: string;
      providerTaskId?: string;
      createdAt: Date;
    }>
  > {
    const tasks = await this.imagesService.taskRepo.find({
      where: { bizId, mode: GenerationMode.TEXT_TO_IMAGE },
      order: { createdAt: 'ASC' },
    });
    return tasks.map((task) => ({
      id: task.id,
      prompt: task.prompt,
      status: task.status,
      angle: task.metadata?.angle, // 从 metadata 中获取角度
      providerTaskId: task.providerTaskId,
      createdAt: task.createdAt,
    }));
  }
}
查询参数

使用这个装饰器,拿到对应的查询参数并在服务端重新命名使用

less 复制代码
@Get('search')
searchUsers(
https//xxx:xxx/user?q=1&page=2&limit=2
  @Query('q') query: string,
  @Query('page') page: number = 1,
  @Query('limit') limit: number = 10
  // 重新命名,可以再服务端拿到参与逻辑编写
) {
  return { query, page, limit };
}

4.2 请求体

使用 DTO 验证请求数据

如下图所示:

  • CreateUserDto 就是一个 DTO,它定义了创建用户时请求体(@Body())应该包含的字段(nameemailpassword)及类型。

  • 作用主要有两个:

    • 规范请求格式:明确告诉前端 "创建用户必须传这些字段",避免无效或错误的数据提交。
    • 类型约束:在 TypeScript 中提供类型检查,确保后端处理数据时的类型安全(比如不会把 email 当成数字处理)。
less 复制代码
// src/users/dto/create-user.dto.ts
export class CreateUserDto {
  name: string;
  email: string;
  password: string;
}
// 在控制器中使用
@Post()
create(@Body() createUserDto: CreateUserDto) {
  return this.usersService.create(createUserDto);
}

4.3 RESTful API 设计

一个标准的 RESTful API 设计应该包含以下端点:

less 复制代码
@Controller('users')
export class UsersController {
  @Get()                 // GET /users - 获取所有用户
  findAll() {}
  @Get(':id')            // GET /users/:id - 获取单个用户
  findOne() {}
  @Post()                // POST /users - 创建用户
  create() {}
  @Put(':id')            // PUT /users/:id - 更新用户
  update() {}
  @Delete(':id')         // DELETE /users/:id - 删除用户
  remove() {}
}

5. 数据验证

5.1 使用 class-validator

NestJS 推荐使用 class-validator 库进行数据验证:

安装依赖
arduino 复制代码
npm install class-validator class-transformer
创建验证 DTO
less 复制代码
// src/users/dto/create-user.dto.ts
import { IsString, IsEmail, IsNotEmpty, MinLength } from 'class-validator';
export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  name: string;
  @IsEmail()
  @IsNotEmpty()
  email: string;
  @IsString()
  @IsNotEmpty()
  @MinLength(6)
  password: string;
}

5.2 全局验证管道

在 main.ts 中配置全局验证管道:

javascript 复制代码
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // 全局验证管道
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true, // 自动移除没有装饰器的属性
    forbidNonWhitelisted: true, // 如果有非白名单属性则抛出错误
    transform: true // 自动转换类型
  }));
  
  await app.listen(3000);
}
bootstrap();

5.3 常用验证装饰器

装饰器 功能
@IsString() 验证是否为字符串
@IsNumber() 验证是否为数字
@IsEmail() 验证是否为邮箱格式
@IsNotEmpty() 验证是否不为空
@MinLength(n) 验证最小长度
@MaxLength(n) 验证最大长度
@IsOptional() 标记为可选

6. 数据库集成

6.1 使用 TypeORM

NestJS 与 TypeORM(一个流行的 ORM 框架)有很好的集成:

安装依赖
bash 复制代码
npm install @nestjs/typeorm typeorm mysql2
配置数据库连接
php 复制代码
// src/app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'nestjs_db',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true, // 开发环境下自动同步数据库结构
      logging: true,
    }),
    UsersModule,
  ],
})
export class AppModule {}

6.2 创建实体

less 复制代码
// src/users/entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
@Entity('users')
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;
  @Column({ length: 50, nullable: false })
  name: string;
  @Column({ unique: true, nullable: false })
  email: string;
  @Column({ nullable: false })
  password: string;
  @Column({ default: 'user' })
  role: string;
  @CreateDateColumn()
  createdAt: Date;
  @UpdateDateColumn()
  updatedAt: Date;
}

6.3 使用 Repository

以下例子是将User 组件导入进来,ioc容器会自动检索其是否有对应的实例,如果有则直接使用已有实例,避免重复注册实例增大性能开销

typescript 复制代码
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}
  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.usersRepository.create(createUserDto);
    return this.usersRepository.save(user);
  }
  async findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }
  async findOne(id: string): Promise<User> {
    return this.usersRepository.findOneBy({ id });
  }
  async update(id: string, updateUserDto: any): Promise<User> {
    await this.usersRepository.update(id, updateUserDto);
    return this.findOne(id);
  }
  async remove(id: string): Promise<void> {
    await this.usersRepository.delete(id);
  }
}

6.4 模块配置

python 复制代码
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}

7. 错误处理

7.1 内置异常

NestJS 提供了多种内置异常类型:

  1. 内置异常(7.1)
  • NotFoundException:资源不存在(对应 404 状态码)
  • BadRequestException:请求参数错误(400)
  • UnauthorizedException:未授权(401)
  • ForbiddenException:权限不足(403)
  • InternalServerErrorException:服务器内部错误(500)

示例中,当通过 id 查询用户时,如果用户不存在,就抛出 NotFoundException,NestJS 会自动返回对应的 404 错误响应。

typescript 复制代码
import { 
  BadRequestException, 
  NotFoundException,
  UnauthorizedException,
  ForbiddenException,
  InternalServerErrorException
} from '@nestjs/common';
@Controller('users')
export class UsersController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    const user = this.usersService.findOne(id);
    
    if (!user) {
      throw new NotFoundException(`User with ID ${id} not found`);
    }
    
    return user;
  }
}
  1. 自定义异常过滤器(7.2)

默认情况下,NestJS 对异常的响应格式比较简单。通过自定义异常过滤器,可以统一错误响应的结构,增加更多上下文信息(如时间戳、请求路径等)。

  • @Catch(HttpException):表示这个过滤器专门捕获 HttpException 及其子类(包括上面的内置异常)。

  • implements ExceptionFilter:实现 ExceptionFilter 接口,必须定义 catch 方法,用于处理捕获到的异常。

  • 逻辑说明:

    • 通过 ArgumentsHost 获取请求(Request)和响应(Response)对象。
    • 从异常中获取状态码(status)和错误信息(message)。
    • 自定义响应格式:包含状态码、时间戳、请求路径、错误信息,让前端能更清晰地理解错误原因。
    typescript 复制代码
    // src/common/filters/http-exception.filter.ts
    import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
    import { Request, Response } from 'express';
    @Catch(HttpException)
    export class HttpExceptionFilter implements ExceptionFilter {
      catch(exception: HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp();
        const response = ctx.getResponse<Response>();
        const request = ctx.getRequest<Request>();
        const status = exception.getStatus();
        response.status(status).json({
          statusCode: status,
          timestamp: new Date().toISOString(),
          path: request.url,
          message: exception.message
        });
      }
    }
  1. 全局异常处理(7.3)

通过 app.useGlobalFilters(new HttpExceptionFilter()) 将自定义的 HttpExceptionFilter 注册为全局过滤器,意味着:

  • 整个应用中所有抛出的 HttpException 及其子类异常,都会被这个过滤器捕获并处理。
  • 无需在每个控制器或服务中重复定义异常处理逻辑,实现了错误响应格式的全局统一。
javascript 复制代码
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // 全局异常过滤器
  app.useGlobalFilters(new HttpExceptionFilter());
  
  await app.listen(3000);
}
bootstrap();

深入扩展

IOC 控制反转与 DI 依赖注入

1.1 控制反转(IoC)概念

控制反转 是一种设计原则,它将对象的创建和依赖关系的管理从应用程序代码转移到容器 中。在 NestJS 中,这个容器就是 IoC 容器。

1.2 依赖注入(DI)原理

依赖注入 是实现 IoC 的一种方式,它允许类从外部接收依赖项,而不是自己创建它们。

typescript 复制代码
// 传统方式 - 紧耦合
class UserService {
  private database = new Database();
  getUser(id: string) {
    return this.database.query(SELECT * FROM users WHERE id = ${id});
  }
}

// 依赖注入 - 松耦合
@Injectable()
class UserService {
  constructor(private database: DatabaseService) {}
  getUser(id: string) {
    return this.database.query(SELECT * FROM users WHERE id = ${id});
  }
}

1.3 NestJS 中的依赖注入

typescript 复制代码
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  getUsers() {
    return ['Alice', 'Bob', 'Charlie'];
  }
}

@Injectable()
export class AuthService {
  constructor(private userService: UserService) {}
  validateUser(username: string) {
    const users = this.userService.getUsers();
    return users.includes(username);
  }
}

1.4 依赖注入的优势

  1. 解耦 代码 - 降低组件间的耦合度
  2. 易于测试 - 可以轻松替换依赖项
  3. 更好的维护性 - 依赖关系集中管理
  4. 可扩展性 - 便于功能扩展和重构
  5. 前置知识 - 装饰器

装饰器

2.1 什么是装饰器

装饰器 是一种特殊类型的声明,它能够被附加到类声明、方法、访问符、属性或参数上。装饰器使用 @expression 形式,其中 expression 必须是一个函数,在运行时被调用,被装饰的声明信息作为参数传入。


2.2 TypeScript 装饰器类型

类装饰器
javascript 复制代码
function Component(constructor: Function) {
  console.log('Component decorator called');
}

@Component
class UserComponent {
  constructor() {
    console.log('UserComponent created');
  }
}
方法装饰器
typescript 复制代码
function Log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log(Calling ${propertyKey} with arguments: ${args});
    return originalMethod.apply(this, args);
  };
  return descriptor;
}

class UserService {
  @Log
  getUser(id: string) {
    return User ${id};
  }
}
属性装饰器
typescript 复制代码
function DefaultValue(value: any) {
  return function(target: any, propertyKey: string) {
    target[propertyKey] = value;
  };
}

class User {
  @DefaultValue('John Doe')
  name: string;
}

2.3 NestJS 中的装饰器

kotlin 复制代码
NestJS 大量使用装饰器来声明各种组件:
import { Controller, Get, Injectable } from '@nestjs/common';

@Injectable()
export class UserService {}

@Controller('users')
export class UserController {
  constructor(private userService: UserService) {}
  @Get()
  getUsers() {
    return this.userService.getUsers();
  }
}

3.NestJS 中间件

3.1 中间件的概念

中间件 是在路由处理程序之前调用的函数。中间件函数可以访问请求对象(req)、响应对象(res)和应用程序的请求 - 响应循环中的下一个中间件函数。

3.2 中间件的类型

函数中间件
vbscript 复制代码
// logger.middleware.ts
import { Request, Response, NextFunction } from 'express';

export function loggerMiddleware(
  req: Request,
  res: Response,
  next: NextFunction
) {
  console.log(${req.method} ${req.path} - ${new Date().toISOString()});
  next();
}
类中间件
typescript 复制代码
// logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(${req.method} ${req.path} - ${new Date().toISOString()});
    // 可以在这里添加更多逻辑
    res.on('finish', () => {
      console.log(Response status: ${res.statusCode});
    });
    next();
  }
}

3.3 中间件的使用

全局中间件
javascript 复制代码
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { loggerMiddleware } from './common/middleware/logger.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 使用函数中间件
  app.use(loggerMiddleware);
  await app.listen(3000);
}
bootstrap();
模块级中间件
typescript 复制代码
// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { UserController } from './user/user.controller';

@Module({
  imports: [],
  controllers: [UserController],
  providers: []
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(UserController);
  }
}
路由级中间件
scss 复制代码
// app.module.ts
@Module({})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('users');
    // 或者指定 HTTP 方法
    consumer
      .apply(AuthMiddleware)
      .forRoutes({ path: 'users', method: RequestMethod.GET });
    // 多个中间件
    consumer
      .apply(Middleware1, Middleware2)
      .forRoutes('posts');
  }
}

3.4 常用中间件示例

认证中间件
typescript 复制代码
// auth.middleware.ts
import { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(private jwtService: JwtService) {}

  use(req: Request, res: Response, next: NextFunction) {
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      throw new UnauthorizedException('Authentication required');
    }
    const token = authHeader.split(' ')[1];
    try {
      const decoded = this.jwtService.verify(token);
      req.user = decoded;
      next();
    } catch (error) {
      throw new UnauthorizedException('Invalid or expired token');
    }
  }
}
请求计时中间件
typescript 复制代码
// timing.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class TimingMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const start = Date.now();
    res.on('finish', () => {
      const duration = Date.now() - start;
      console.log(${req.method} ${req.path} - ${duration}ms);
    });
    next();
  }
}
CORS 中间件
javascript 复制代码
// main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({
    origin: ['http://localhost:3000', 'https://myapp.com'],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    allowedHeaders: ['Content-Type', 'Authorization']
  });
  await app.listen(3000);
}
bootstrap();
相关推荐
孟祥_成都1 天前
深入 Nestjs 底层概念(1):依赖注入和面向切面编程 AOP
前端·node.js·nestjs
濮水大叔10 天前
VonaJS: 基于winston的Logger日志系统
typescript·nodejs·nestjs
濮水大叔13 天前
VonaJS: 序列化/数据脱敏(上)
typescript·node.js·nestjs
濮水大叔13 天前
VonaJS: 序列化/数据脱敏(下)
typescript·nodejs·nestjs
江湖人称小鱼哥14 天前
AsyncLocalStorage 请求上下文实现
nestjs
濮水大叔15 天前
VonaJS: 直观好用的分布式锁
typescript·node.js·nestjs