主题
Module、Controller 与依赖注入
Nest 应用由根模块开始构建依赖图。模块默认封装其 Provider;其他模块只能注入本模块的 Provider,或已由导入模块显式导出的 Provider。
按业务域组织
一个业务域通常包含模块、控制器、服务和 DTO。控制器负责协议层,服务负责业务逻辑;数据库访问可置于服务或独立的 Repository/Provider 中。
bash
src
├── app.module.ts
└── users
├── dto
│ └── create-user.dto.ts
├── users.controller.ts
├── users.module.ts
└── users.service.tsModule
imports 引入其他模块导出的能力,providers 注册可注入对象,controllers 注册 HTTP 控制器,exports 定义模块对外暴露的 Provider。
ts
import { Module } from '@nestjs/common'
import { UsersController } from './users.controller'
import { UsersService } from './users.service'
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}全局模块会隐藏依赖来源,应只用于配置、日志等基础设施;业务模块优先显式导入。
根模块只会发现已导入的功能模块。新增 UsersModule 后,必须将它加入 AppModule 的 imports,否则其中的 Controller 和 Provider 不会生效。
ts
import { Module } from '@nestjs/common'
import { UsersModule } from './users/users.module'
@Module({
imports: [UsersModule],
})
export class AppModule {}Controller
控制器使用装饰器匹配路由。@Get()、@Post()、@Put()、@Patch()、@Delete() 分别对应常见 HTTP 方法;@Query()、@Param()、@Body() 用于提取请求输入。
保持控制器轻薄:接收输入、调用服务、返回结果;不在其中编写业务规则或数据库查询。静态路由必须写在参数路由前面,否则 /users/all 会被 :id 当作 id = 'all' 匹配。
ts
import { Body, Controller, Get, Param, ParseIntPipe, Post, Query } from '@nestjs/common'
import { CreateUserDto } from './dto/create-user.dto'
import { UsersService } from './users.service'
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get('all')
findAll(@Query('page', ParseIntPipe) page: number) {
return this.usersService.findAll(page)
}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findOne(id)
}
@Post()
create(@Body() input: CreateUserDto) {
return this.usersService.create(input)
}
}DTO 必须是 class,不能只写 TypeScript interface:接口在运行时会被擦除,ValidationPipe 无法读取 class-validator 的装饰器元数据。更新 DTO 可由创建 DTO 派生:
ts
import { PartialType } from '@nestjs/mapped-types'
import { CreateUserDto } from './create-user.dto'
export class UpdateUserDto extends PartialType(CreateUserDto) {}Provider 与依赖注入
Provider 是由 Nest 容器创建和管理的依赖。类 Provider 以类本身为 token;构造函数参数按 token 注入。不要在 Controller 或 Service 中直接 new UsersService()、new PrismaClient();这样会绕开容器,无法共享配置、替换依赖或测试 Mock。
ts
import { Injectable, NotFoundException } from '@nestjs/common'
import { CreateUserDto } from './dto/create-user.dto'
@Injectable()
export class UsersService {
private readonly users = new Map<number, CreateUserDto>()
findOne(id: number) {
const user = this.users.get(id)
if (!user) throw new NotFoundException('用户不存在')
return { id, ...user }
}
create(input: CreateUserDto) {
const id = this.users.size + 1
this.users.set(id, input)
return { id, ...input }
}
}@Injectable() 让 Nest 能读取构造函数依赖并创建实例。依赖链由容器解析,例如 UsersController → UsersService → AppLogger;Logger 等依赖也要在 Module 的 providers 中注册。
ts
import { Injectable, Logger } from '@nestjs/common'
@Injectable()
export class UsersService {
constructor(private readonly logger: Logger) {}
findAll() {
this.logger.log('读取用户列表')
return []
}
}涉及接口、常量或多个实现时,使用显式 token 与 @Inject(token);token 应集中定义,避免字符串散落在业务代码中。
