话不多说 从新建文件夹开始 一步一步爬上去 前端部分不展开 默认有一定的前端基础 源码仓库 需要自取
安装NestJS CLI
首先,需要全局安装NestJS CLI(命令行工具),它可以帮助我们快速创建和管理NestJS项目。
bash
npm install -g @nestjs/cli
在当前目录创建项目
安装完成后,可以使用NestJS CLI在当前目录创建一个新项目。
sql
nest new . --skip-git
这里的参数解释:
- . 表示在当前目录创建项目,而不是创建新目录
- --skip-git 表示跳过Git初始化(如果想使用Git,可以去掉这个参数)
选择包管理器 创建过程
执行命令后,系统会提示选择一个包管理器(npm、yarn或pnpm)。可以根据自己的喜好选择。
创建过程中,CLI会自动为完成以下工作:
- 生成标准的NestJS项目结构
- 创建必要的配置文件
- 安装依赖包 完成后,就可以开始探索NestJS项目的结构和基本概念了。
运行NestJS项目
成功创建了NestJS项目并安装了依赖。现在开始先运行项目,确保一切正常工作。
在终端中执行以下命令来启动开发服务器: pnpm run start:dev
这个命令会启动一个开发服务器,并且会在修改代码时自动重新编译和重启服务器,非常适合开发阶段使用。
如果一切正常,应该会在终端中看到类似下面的输出:
ini
[Nest] 10312 - 2025/08/28 17:49:32 LOG [NestFactory] Starting Nest application...
[Nest] 10312 - 2025/08/28 17:49:32 LOG [InstanceLoader] UsersModule dependencies initialized +9ms
[Nest] 10312 - 2025/08/28 17:49:32 LOG [InstanceLoader] AppModule dependencies initialized +0ms
[Nest] 10312 - 2025/08/28 17:49:32 LOG [RoutesResolver] AppController {/}: +7ms
[Nest] 10312 - 2025/08/28 17:49:32 LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 10312 - 2025/08/28 17:49:32 LOG [RouterExplorer] Mapped {/greet, GET} route +0ms
[Nest] 10312 - 2025/08/28 17:49:32 LOG [NestApplication] Nest application successfully started +2ms
这表示服务器已经成功启动在3000端口。可以打开浏览器,访问 http://localhost:3000 来查看默认的欢迎页面。
项目结构简介
现在来了解一下NestJS项目的基本结构:
- src/main.ts - 应用程序的入口文件,负责创建Nest应用实例并启动它
- src/app.module.ts - 应用程序的根模块,包含了其他模块的导入和配置
- src/app.controller.ts - 应用程序的根控制器,处理HTTP请求
- src/app.service.ts - 应用程序的根服务,包含业务逻辑
- src/app.controller.spec.ts - 根控制器的测试文件 NestJS使用了模块化的架构,通过控制器(Controllers)处理HTTP请求,服务(Services)包含业务逻辑,模块(Modules)组织代码结构。
NestJS的核心概念
NestJS采用了模块化的架构,主要包含以下核心概念:
- 模块(Modules) :组织代码结构,将相关的控制器、服务等组合在一起
- 控制器(Controllers) :处理HTTP请求,定义路由
- 服务(Services) :包含业务逻辑,被控制器调用
- 提供者(Providers) :可以被注入到其他组件的依赖项
修改默认控制器进行牛刀小试
xianzai来修改默认的控制器和服务,创建一个简单的API。首先,查看当前的app.service.ts和app.controller.ts文件,然后进行修改。
修改服务 (AppService)
添加一个新的方法来支持个性化问候:
ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
// 添加个性化问候方法
getGreeting(name: string): string {
return `Hello, ${name}! Welcome to
NestJS.`;
}
}
修改控制器 (AppController)
接下来,修改 app.controller.ts
,添加一个新的路由来处理个性化问候请求:
ts
import { Controller, Get, Query } from
'@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService:
AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
// 添加新的GET路由,支持查询参数name
@Get('greet')
getGreeting(@Query('name') name: string):
string {
// 如果没有提供name参数,使用默认值'Guest'
return this.appService.getGreeting
(name || 'Guest');
}
}
代码解释
-
服务修改 :添加了
getGreeting
方法,它接受一个name参数并返回个性化问候语。 -
控制器修改 :
- 导入了 Query 装饰器,用于获取URL查询参数
- 添加了
getGreeting
方法,并使用 @Get('greet') 装饰器将其映射到 /greet 路由 - 使用 @Query('name') 获取URL中的name参数
- 实现了参数默认值逻辑,如果没有提供name参数,默认使用'Guest'
测试新API
保存修改后,NestJS开发服务器会自动重启。现在您可以测试两个API端点:
- 原有的根路由: http://localhost:3000 ,仍然显示"Hello World!"
- 新的个性化问候路由: http://localhost:3000/greet?name=YourName ,将显示"Hello, YourName! Welcome to NestJS." 尝试修改URL中的name参数,看看返回结果如何变化。这个简单的例子展示了NestJS中控制器和服务的基本工作方式,以及如何处理请求参数。
NestJS模块化
接下来学习NestJS的模块化特性,这是NestJS架构的核心概念之一。
随着应用程序的增长,将所有代码放在一个文件中会变得难以维护。NestJS通过模块化来解决这个问题,每个模块可以包含自己的控制器、服务和其他组件。
创建一个用户模块来管理用户相关的功能:
步骤1:使用Nest CLI创建用户模块
在终端中执行以下命令: nest generate module users
这个命令会创建一个新的用户模块,包括:
- users.module.ts 文件
- users 目录
- 自动将新模块导入到根模块 app.module.ts
步骤2:创建用户控制器
接着,在用户模块中创建一个控制器: nest generate controller users
步骤3:创建用户服务
最后,创建一个用户服务: nest generate service users
生成的文件结构
执行完这些命令后,您的项目结构将变为:
arduino
src/
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── main.ts
└── users/
├── users.controller.spec.ts
├── users.controller.ts
├── users.module.ts
├── users.service.spec.ts
└── users.service.ts
理解模块导入
打开 app.module.ts
,会看到Nest CLI已经自动将 UsersModule 添加到了导入列表中:
ts
import { Module } from '@nestjs/common';
import { AppController } from './app.
controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.
module'; // 自动添加的导入
@Module({
imports: [UsersModule], // 自动添加到导入数组
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
下一步 完善users模块
创建完用户模块后,我们将在新模块中实现用户相关的API端点。这将帮助我们理解NestJS是如何通过模块来组织代码,以及不同模块之间如何交互的。
定义用户数据结构
在用户模块中,需要定义用户的数据结构。先创建一个用户接口: 在users.service.ts
ts
import { Injectable } from '@nestjs/common';
// 定义用户接口
export interface User {
id: number;
name: string;
email: string;
}
@Injectable()
export class UsersService {
// 模拟数据库存储
private users: User[] = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
{ id: 3, name: '王五', email: 'wangwu@example.com' },
];
// 获取所有用户
findAll(): User[] {
return this.users;
}
// 根据ID获取单个用户
findOne(id: number): User | undefined {
return this.users.find(user => user.id === id);
}
// 创建新用户
create(user: Omit<User, 'id'>): User {
const newUser = {
id: this.users.length + 1,
...user
};
this.users.push(newUser);
return newUser;
}
// 更新用户信息
update(id: number, updatedUser: Partial<User>): User | undefined {
const index = this.users.findIndex(user => user.id === id);
if (index !== -1) {
this.users[index] = {
...this.users[index],
...updatedUser
};
return this.users[index];
}
return undefined;
}
// 删除用户
remove(id: number): boolean {
const index = this.users.findIndex(user => user.id === id);
if (index !== -1) {
this.users.splice(index, 1);
return true;
}
return false;
}
}
接下来,实现用户控制器
现在在控制器中添加API端点来处理HTTP请求: users.controller.ts
ts
import { Controller, Get, Post, Put, Delete, Param, Body, NotFoundException } from '@nestjs/common';
import { UsersService, User } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
// GET /users - 获取所有用户
@Get()
findAll(): User[] {
return this.usersService.findAll();
}
// GET /users/:id - 获取单个用户
@Get(':id')
findOne(@Param('id') id: string): User {
const user = this.usersService.findOne(Number(id));
if (!user) {
throw new NotFoundException(`用户 ID ${id} 不存在`);
}
return user;
}
// POST /users - 创建新用户
@Post()
create(@Body() user: Omit<User, 'id'>): User {
return this.usersService.create(user);
}
// PUT /users/:id - 更新用户信息
@Put(':id')
update(@Param('id') id: string, @Body() updatedUser: Partial<User>): User {
const user = this.usersService.update(Number(id), updatedUser);
if (!user) {
throw new NotFoundException(`用户 ID ${id} 不存在`);
}
return user;
}
// DELETE /users/:id - 删除用户
@Delete(':id')
remove(@Param('id') id: string): { message: string } {
const result = this.usersService.remove(Number(id));
if (!result) {
throw new NotFoundException(`用户 ID ${id} 不存在`);
}
return { message: '用户已成功删除' };
}
}
更新用户模块
现在我们需要更新用户模块,将控制器和服务注册到模块中:users.module.ts
ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
controllers: [UsersController],
providers: [UsersService]
})
export class UsersModule {}
理解代码结构
我们刚刚实现了一个简单的用户管理API,包含以下关键点:
-
模块 (Module) -
users.module.ts
用于组织和注册相关组件 -
控制器 (Controller) -
users.controller.ts
处理HTTP请求并返回响应- 使用装饰器定义路由(@Get, @Post, @Put, @Delete)
- 使用@Param获取URL参数
- 使用@Body获取请求体数据
-
服务 (Service) -
users.service.ts
包含业务逻辑- 使用@Injectable装饰器标记为可注入的服务
- 实现了CRUD操作(创建、读取、更新、删除)
- 使用数组模拟数据库存储
测试API和开启cors
现在可以使用API测试工具(如Postman、Insomnia或curl)测试这些端点:
- 获取所有用户: GET http://localhost:3000/users
- 获取单个用户: GET http://localhost:3000/users/1
- 创建新用户: POST http://localhost:3000/users (需要JSON请求体)
- 更新用户: PUT http://localhost:3000/users/1 (需要JSON请求体)
- 删除用户: DELETE http://localhost:3000/users/1
或者在前端项目中开启代理访问 也是能成功获取到接口的 (前端部分暂略)
后端开启代理则在main.ts中
ts
// // // 启用CORS - 这是最基本的配置
// app.enableCors();
// 高级CORS配置
app.enableCors({
// 允许的源 - 可以是字符串、字符串数组或函数
origin: [
'http://localhost:8080', // 前端开发服务器地址
'http://127.0.0.1:8080',
],
// 允许的HTTP方法
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
// 允许携带凭证(如cookies)
credentials: true,
// 允许的请求头
allowedHeaders: 'Content-Type, Accept, Authorization',
// 暴露的响应头
exposedHeaders: 'X-Custom-Header',
//预检请求的有效期(秒)
maxAge: 86400,
});

不过可以看到 目前还没对前端传入数据进行校验 所以肯定是不行的
现在来为用户模块添加数据验证功能,哲理将使用NestJS推荐的 class-validator 库来实现输入数据验证。
第一步:安装必要依赖
首先需要安装两个关键依赖包: pnpm install --save class-validator class-transformer
第二步:创建数据传输对象(DTO)
DTO用于规范API接口的输入输出数据格式,我们创建用户相关的DTO:
create-user.dto.ts
ts
import { IsString, IsEmail, MinLength }
from 'class-validator';
// 创建用户DTO - 用于规范创建用户时的请求数据格式
export class CreateUserDto {
// @IsString() 验证该字段必须是字符串类型
@IsString()
// @MinLength(2) 验证字符串长度至少为2
@MinLength(2, { message: '用户名长度不能少于
2个字符' })
name: string;
// @IsEmail() 验证该字段必须是合法的邮箱格式
@IsEmail({}, { message: '请输入有效的邮箱地
址' })
email: string;
}
update-user.dto.ts
ts
import { IsString, IsEmail, MinLength,
IsOptional } from 'class-validator';
// 更新用户DTO - 用于规范更新用户时的请求数据格式
export class UpdateUserDto {
// @IsOptional() 表示该字段是可选的
@IsOptional()
@IsString()
@MinLength(2, { message: '用户名长度不能少于
2个字符' })
name?: string;
@IsOptional()
@IsEmail({}, { message: '请输入有效的邮箱地
址' })
email?: string;
}
第三步:修改用户控制器使用DTO
users.controller.ts
ts
import { Controller, Get, Post, Body,
Patch, Param, Delete, Query } from '@nestjs/
common';
import { UsersService } from './users.
service';
// 导入刚才创建的DTO
import { CreateUserDto } from './dto/
create-user.dto';
import { UpdateUserDto } from './dto/
update-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly
usersService: UsersService) {}
// 创建用户 - 使用CreateUserDto验证请求体
@Post()
// @Body() 装饰器用于获取请求体数据,并使用
CreateUserDto进行验证
create(@Body() createUserDto:
CreateUserDto) {
return this.usersService.create
(createUserDto);
}
// ... 现有代码 ...
// 更新用户 - 使用UpdateUserDto验证请求体
@Patch(':id')
update(
@Param('id') id: string,
// 使用UpdateUserDto验证更新数据
@Body() updateUserDto: UpdateUserDto
) {
return this.usersService.update(+id,
updateUserDto);
}
// ... 现有代码 ...
}
第四步:更新用户服务
users.service.ts
ts
import { Injectable } from '@nestjs/common';
// 导入DTO类型
import { CreateUserDto } from './dto/
create-user.dto';
import { UpdateUserDto } from './dto/
update-user.dto';
// 定义User接口
export interface User {
id: number;
name: string;
email: string;
}
@Injectable()
export class UsersService {
// 模拟数据库 - 存储用户数据的数组
private users: User[] = [
{ id: 1, name: '张三', email:
'zhangsan@example.com' },
{ id: 2, name: '李四', email:
'lisi@example.com' },
{ id: 3, name: '王五', email:
'wangwu@example.com' },
];
// 创建用户
create(createUserDto: CreateUserDto):
User {
// 生成新用户ID (实际项目中通常由数据库自动生
成)
const newUser: User = {
id: this.users.length + 1,
// 直接使用DTO中的数据,因为已经通过验证
...createUserDto
};
this.users.push(newUser);
return newUser;
}
// ... 现有代码 ...
// 更新用户
update(id: number, updateUserDto:
UpdateUserDto): User {
const index = this.users.findIndex(user
=> user.id === id);
if (index === -1) {
// 在实际项目中,这里应该抛出
NotFoundException
throw new Error(`User with ID ${id}
not found`);
}
// 合并更新数据 - 只更新提供的字段
this.users[index] = {
...this.users[index],
...updateUserDto
};
return this.users[index];
}
// ... 现有代码 ...
}
第五步:启用全局验证管道
main.ts
ts
import { NestFactory } from '@nestjs/core';
// 导入ValidationPipe
import { ValidationPipe } from '@nestjs/
common';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create
(AppModule);
// 启用全局验证管道
app.useGlobalPipes(
new ValidationPipe({
// 启用白名单模式 - 自动移除DTO中未定义的
属性
whitelist: true,
// 禁止非白名单属性 - 如果有未定义的属性,
将抛出错误
forbidNonWhitelisted: true,
// 自动转换输入数据类型
transform: true,
})
);
await app.listen(3000);
}
bootstrap();
代码解释
-
DTO (数据传输对象) :
- 用于验证和规范API输入数据
- 使用 class-validator 装饰器定义验证规则
- CreateUserDto 用于创建用户,所有字段都是必填的
- UpdateUserDto 用于更新用户,所有字段都是可选的
-
ValidationPipe (验证管道) :
- whitelist: true :自动过滤掉DTO中未定义的属性
- forbidNonWhitelisted: true :如果请求中包含未定义的属性,将返回400错误
- transform: true :自动将请求数据转换为DTO类的实例
-
验证装饰器 :
- @IsString() :验证字段必须是字符串
- @IsEmail() :验证字段必须是合法邮箱格式
- @MinLength(n) :验证字符串长度至少为n
- @IsOptional() :标记字段为可选
测试验证功能
现在可以测试这些验证规则是否生效:
- 尝试创建用户时不提供name字段或提供无效邮箱
- 尝试更新用户时提供DTO中未定义的字段
- 提供符合验证规则的数据,查看是否能成功创建/更新

事实证明现在接口传参验证已经生效
集成数据库 (TypeORM + SQLite)
接下来,我们可以将内存存储替换为实际的数据库。使用TypeORM和SQLite(轻量级,适合学习):
安装必要依赖
bash
npm install @nestjs/typeorm typeorm sqlite3
修改app.module.ts以集成数据库:
ts
import { Module } from '@nestjs/common';
import { AppController } from './app.
controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.
module';
// 导入TypeOrmModule,用于配置数据库连接
import { TypeOrmModule } from '@nestjs/
typeorm';
@Module({
imports: [
// 配置TypeORM连接
TypeOrmModule.forRoot({
// type: 'sqlite' - 指定使用SQLite数据
库
type: 'sqlite',
// database: 'db.sqlite' - 指定数据库文
件的名称和位置
database: 'db.sqlite',
// entities: [] - 指定要映射到数据库表的
实体类
// __dirname + '/**/*.entity{.ts,.js}
' 表示自动扫描所有以.entity.ts或.entity.
js结尾的文件
entities: [__dirname + '/**/*.entity{.
ts,.js}'],
// synchronize: true - 自动创建数据库表
(仅开发环境使用)
// 注意:在生产环境中应设置为false,避免意
外删除数据
synchronize: true,
// logging: true - 记录SQL查询日志,有助
于调试
logging: true,
}),
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
步骤1: 创建用户实体 (Entity)
现在我们需要创建一个用户实体来映射数据库表。实体是一个类,它定义了数据库表的结构: user.entity.ts
ts
// 导入TypeORM的装饰器
import { Entity, PrimaryGeneratedColumn,
Column } from 'typeorm';
// @Entity() 装饰器标记这个类为一个实体,
TypeORM会为它创建对应的数据库表
@Entity()
export class User {
// @PrimaryGeneratedColumn() 标记这个字段为
主键,并且值会自动生成(自增)
@PrimaryGeneratedColumn()
id: number;
// @Column() 标记这个字段为表中的一列
@Column()
name: string;
// @Column({ unique: true }) 表示这个字段的
值在表中必须是唯一的
// 这对于邮箱字段很重要,可以防止多个用户使用相
同的邮箱
@Column({ unique: true })
email: string;
}
步骤2: 修改UsersModule以使用TypeORM
接下来,我们需要修改UsersModule以导入和使用TypeORM: users.module.ts
ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.
service';
import { UsersController } from './users.
controller';
// 导入TypeOrmModule和我们刚刚创建的User实体
import { TypeOrmModule } from '@nestjs/
typeorm';
import { User } from './entities/user.
entity';
@Module({
// TypeOrmModule.forFeature([User]) 导入
User实体,使其在当前模块中可用
// 这会自动创建一个UserRepository,我们可以在
服务中注入它
imports: [TypeOrmModule.forFeature
([User])],
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}
步骤3: 更新UsersService以使用TypeORM Repository
现在,让我们更新UsersService以使用TypeORM的Repository来操作数据库,而不是使用内存数组: users.service.ts
ts
import { Injectable, NotFoundException }
from '@nestjs/common';
import { CreateUserDto } from './dto/
create-user.dto';
import { UpdateUserDto } from './dto/
update-user.dto';
// 导入User实体和TypeORM相关的类
import { User } from './entities/user.
entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/
typeorm';
// @Injectable() 装饰器标记这个类为一个可以被注
入的服务
@Injectable()
export class UsersService {
// 构造函数中注入UserRepository
// @InjectRepository(User) 装饰器告诉NestJS
我们需要User实体的Repository
constructor(
@InjectRepository(User)
private usersRepository:
Repository<User>,
) {}
// 获取所有用户
// Promise<User[]> 表示这个方法返回一个
Promise,最终解析为User对象的数组
async findAll(): Promise<User[]> {
// find() 方法从数据库中检索所有用户记录
return this.usersRepository.find();
}
// 根据ID获取单个用户
async findOne(id: number): Promise<User> {
// findOneBy({ id }) 方法根据ID查找单个用
户
const user = await this.usersRepository.
findOneBy({ id });
// 如果找不到用户,抛出NotFoundException异
常
if (!user) {
throw new NotFoundException(`用户 ID $
{id} 不存在`);
}
return user;
}
// 创建用户
async create(createUserDto: CreateUserDto)
: Promise<User> {
// create() 方法创建一个新的User实体实例,
但不会保存到数据库
const newUser = this.usersRepository.
create(createUserDto);
// save() 方法将新用户保存到数据库并返回保存
后的用户对象(包含自动生成的ID)
return this.usersRepository.save
(newUser);
}
// 更新用户信息
async update(id: number, updateUserDto:
UpdateUserDto): Promise<User> {
// 先调用findOne()检查用户是否存在,如果不存
在会自动抛出异常
const user = await this.findOne(id);
// merge() 方法将updateUserDto中的属性合并
到user对象中
this.usersRepository.merge(user,
updateUserDto);
// save() 方法将更新后的用户保存到数据库
return this.usersRepository.save(user);
}
// 删除用户
async remove(id: number): Promise<void> {
// delete() 方法从数据库中删除指定ID的用户
const result = await this.
usersRepository.delete(id);
// 如果没有行被影响(即用户不存在),抛出
NotFoundException异常
if (result.affected === 0) {
throw new NotFoundException(`用户 ID $
{id} 不存在`);
}
}
}
步骤4: 更新UsersController以支持异步操作
最后,让我们更新UsersController以支持异步操作(使用async/await): users.controller.ts
ts
import {
Controller,
Get,
Post,
Put,
Delete,
Param,
Body,
NotFoundException,
} from '@nestjs/common';
import { UsersService } from './users.
service';
import { CreateUserDto } from './dto/
create-user.dto';
import { UpdateUserDto } from './dto/
update-user.dto';
// 导入User实体
import { User } from './entities/user.
entity';
// @Controller('users') 装饰器标记这个类为控制
器,处理以/users开头的路由
@Controller('users')
export class UsersController {
// 构造函数中注入UsersService
constructor(private readonly
usersService: UsersService) {}
// GET /users - 获取所有用户
@Get()
// async 关键字表示这个方法是异步的
async findAll(): Promise<User[]> {
// await 关键字等待usersService.findAll()
方法完成
return await this.usersService.findAll
();
}
// GET /users/:id - 获取单个用户
@Get(':id')
async findOne(@Param('id') id: string):
Promise<User> {
try {
// 将字符串类型的id转换为数字并调用服务的
findOne方法
return await this.usersService.findOne
(Number(id));
} catch (error) {
// 捕获可能的异常并返回友好的错误信息
throw new NotFoundException(`用户 ID $
{id} 不存在`);
}
}
// POST /users - 创建用户
@Post()
async create(@Body() createUserDto:
CreateUserDto): Promise<User> {
// @Body() 装饰器从请求体中提取数据并自动转
换为CreateUserDto类型
return await this.usersService.create
(createUserDto);
}
// PUT /users/:id - 更新用户信息
@Put(':id')
async update(
@Param('id') id: string,
@Body() updateUserDto: UpdateUserDto,
): Promise<User> {
try {
// 调用服务的update方法更新用户信息
return await this.usersService.update
(+id, updateUserDto);
} catch (error) {
throw new NotFoundException(`用户 ID $
{id} 不存在`);
}
}
// DELETE /users/:id - 删除用户
@Delete(':id')
async remove(@Param('id') id: string):
Promise<{ message: string }> {
try {
await this.usersService.remove(Number
(id));
// 返回成功消息
return { message: '用户已成功删除' };
} catch (error) {
throw new NotFoundException(`用户 ID $
{id} 不存在`);
}
}
}
关键概念解释
-
异步操作 (async/await) :
- 异步操作允许程序在等待长时间运行的任务(如数据库查询)完成时继续执行其他代码
- async 关键字标记一个函数为异步函数
- await 关键字等待一个Promise完成并返回其结果
-
TypeORM :
- TypeORM是一个ORM(对象关系映射)框架,它允许你使用JavaScript/TypeScript对象来操作数据库,而不是直接编写SQL
- Repository模式是TypeORM的核心概念,每个实体都有一个对应的Repository,用于执行数据库操作
-
实体 (Entity) :
- 实体是一个类,它映射到数据库中的一个表
- 实体的每个属性映射到表中的一个列
-
DTO (数据传输对象) :
- DTO用于定义在应用程序不同层之间传输的数据结构
- 它们通常用于验证输入数据并限制传输的数据字段
-
依赖注入 :
- 依赖注入是NestJS的核心概念,它允许你将服务和其他组件注入到控制器和其他服务中
- 这使得代码更加模块化、可测试和可维护
运行与测试
现在,已经将内存存储替换为实际的数据库,并更新了代码以支持异步操作。 完成以上修改后,你可以运行项目并测试新功能:


OK 现在已经成gong建立数据库实现数据持久化了
接下来我们将实现两个重要功能:统一API响应格式和请求参数处理。 管道和过滤器和拦截器的目录

统一API响应格式
我们将创建一个 拦截器(Interceptor) 来统一所有API的响应格式。拦截器可以在请求处理完成后对响应进行转换。
步骤1: 创建响应格式拦截器
ts
import { Injectable, NestInterceptor,
ExecutionContext, CallHandler, HttpStatus }
from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
// 定义统一响应格式的接口
interface ApiResponse<T> {
success: boolean;
data: T;
message: string;
statusCode: number;
}
// @Injectable() 标记这是一个可注入的服务
@Injectable()
export class TransformInterceptor<T>
implements NestInterceptor<T,
ApiResponse<T>> {
// intercept方法是拦截器的核心方法
// context: 包含请求的上下文信息
// next: 用于将请求传递给下一个拦截器或最终的处
理程序
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<ApiResponse<T>> {
// 通过管道操作符处理响应
return next.handle().pipe(
map(data => {
// 获取当前请求的响应对象
const response = context.
switchToHttp().getResponse();
// 返回统一格式的响应
return {
success: true,
// 实际响应数据
data: data || null,
// 成功消息,默认为空字符串
message: '',
// HTTP状态码
statusCode: response.statusCode
};
}),
);
}
}
步骤2: 创建异常响应格式过滤器
我们还需要统一异常响应的格式,创建一个异常过滤器:
ts
import { ExceptionFilter, Catch,
ArgumentsHost, HttpException } from
'@nestjs/common';
import { Response } from 'express';
// @Catch(HttpException) 表示这个过滤器捕获所有
HttpException及其子类
@Catch(HttpException)
export class HttpExceptionFilter implements
ExceptionFilter {
catch(exception: HttpException, host:
ArgumentsHost) {
// 获取请求上下文
const ctx = host.switchToHttp();
const response = ctx.
getResponse<Response>();
// 获取异常状态码和响应体
const status = exception.getStatus();
const exceptionResponse = exception.
getResponse();
// 统一异常响应格式
response.status(status).json({
success: false,
data: null,
// 异常消息
message: typeof exceptionResponse ===
'object'
? (exceptionResponse as any).
message || exception.message
: exceptionResponse || exception.
message,
statusCode: status
});
}
}
步骤3: 在main.ts中全局注册拦截器和过滤器
ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/
common';
// 导入我们创建的拦截器和过滤器
import { TransformInterceptor } from './
common/interceptors/transform.interceptor';
import { HttpExceptionFilter } from './
common/filters/http-exception.filter';
async function bootstrap() {
const app = await NestFactory.create
(AppModule);
// 全局注册验证管道
app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}));
// 全局注册响应格式拦截器
app.useGlobalInterceptors(new
TransformInterceptor());
// 全局注册异常过滤器
app.useGlobalFilters(new
HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
请求参数处理
除了已有的DTO验证外,我们可以添加参数过滤和转换功能。这里我们创建一个自定义管道来处理请求参数。
步骤1: 创建参数处理管道
ts
import { PipeTransform, Injectable,
ArgumentMetadata } from '@nestjs/common';
// 自定义管道实现参数转换和过滤
@Injectable()
export class ParamTransformPipe implements
PipeTransform {
// value: 当前处理的参数值
// metadata: 参数元数据,包含参数类型和数据
transform(value: any, metadata:
ArgumentMetadata) {
// 根据参数类型进行不同处理
switch (metadata.type) {
case 'body':
return this.transformBody(value);
case 'query':
return this.transformQuery(value);
case 'param':
return this.transformParam(value);
default:
return value;
}
}
// 处理请求体参数
private transformBody(body: any): any {
if (typeof body !== 'object' || body
=== null) {
return body;
}
// 示例:移除所有空字符串属性
const result = { ...body };
for (const key in result) {
if (result[key] === '') {
delete result[key];
}
}
return result;
}
// 处理查询参数
private transformQuery(query: any): any {
if (typeof query !== 'object' || query
=== null) {
return query;
}
// 示例:将查询参数中的字符串'true'/'false'
转换为布尔值
const result = { ...query };
for (const key in result) {
if (result[key] === 'true') {
result[key] = true;
} else if (result[key] === 'false') {
result[key] = false;
}
}
return result;
}
// 处理路径参数
private transformParam(param: any): any {
// 示例:移除路径参数中的空格
if (typeof param === 'string') {
return param.trim();
}
return param;
}
}
步骤2: 在main.ts中注册参数处理管道
ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/
common';
import { TransformInterceptor } from './
common/interceptors/transform.interceptor';
import { HttpExceptionFilter } from './
common/filters/http-exception.filter';
// 导入参数处理管道
import { ParamTransformPipe } from './
common/pipes/param-transform.pipe';
async function bootstrap() {
const app = await NestFactory.create
(AppModule);
// 全局注册参数处理管道
app.useGlobalPipes(
new ParamTransformPipe(),
// 已有的验证管道
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
})
);
app.useGlobalInterceptors(new
TransformInterceptor());
app.useGlobalFilters(new
HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
功能测试与验证


接口数据格式和异常处理都处理完后 然后在前端这边再进行调整下(前端部分略)
调整完成后的效果如下 这样子就可以了

后续有空继续完善。。。