提供者与服务
Service 是最常见的 Provider,封装业务逻辑。见 module。
提供者(Providers)是 NestJS 依赖注入体系的核心。服务(Service)是一种最常见的提供者,通常使用 @Injectable() 装饰器。除了服务类,NestJS 还支持多种自定义提供者方式,满足不同场景的注入需求。
一、服务(Service)
服务用于封装业务逻辑,如数据验证、数据库操作、调用外部 API 等。控制器负责处理请求,服务负责执行业务,保持关注点分离。
1. 使用 @Injectable() 装饰器
@Injectable() 标记一个类可以被 NestJS 的 IoC 容器管理,并注入到其他类中。
// users.service.ts
import { Injectable } from "@nestjs/common";
@Injectable()
export class UsersService {
private users = [{ id: 1, name: "John" }];
findOne(id: number) {
return this.users.find((user) => user.id === id);
}
create(user: { name: string }) {
const newUser = { id: Date.now(), ...user };
this.users.push(newUser);
return newUser;
}
}
2. 在控制器中注入服务
通过构造函数注入,NestJS 会自动实例化并提供 UsersService。
// users.controller.ts
import { Controller, Get, Param, Post, Body } from "@nestjs/common";
import { UsersService } from "./users.service";
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get(":id")
findOne(@Param("id") id: number) {
return this.usersService.findOne(id);
}
@Post()
create(@Body() body: { name: string }) {
return this.usersService.create(body);
}
}
3. 服务之间的相互依赖
服务也可以注入其他服务,形成清晰的层级。只需在构造函数中声明即可。
@Injectable()
export class OrderService {
constructor(private readonly usersService: UsersService) {}
getOrderWithUser(orderId: number) {
const order = this.findOrder(orderId);
const user = this.usersService.findOne(order.userId);
return { order, user };
}
}
二、自定义提供者
除了类提供者(默认的 @Injectable() 类),NestJS 允许使用多种提供者定义方式,通过 @Module 装饰器的 providers 数组配置。
提供者定义的基本结构包含 provide 令牌(token)和具体的提供者策略。
1. 值提供者(useValue)
用于注入常量、第三方库实例或配置对象。
// config.module.ts
import { Module } from "@nestjs/common";
const config = { apiUrl: "https://api.example.com", timeout: 5000 };
@Module({
providers: [{ provide: "CONFIG", useValue: config }],
})
export class ConfigModule {}
在其他类中通过 @Inject('CONFIG') 注入:
@Injectable()
export class ApiService {
constructor(@Inject("CONFIG") private config: { apiUrl: string; timeout: number }) {}
}
2. 类提供者(useClass)
允许指定某个令牌实际使用的实现类。这在需要替换实现(如测试 mock、多环境适配)时非常有用。
// 定义抽象类或接口
export interface Mailer {
send(to: string, subject: string, body: string): void;
}
export class SmtpMailer implements Mailer {
/* ... */
}
export class MockMailer implements Mailer {
/* ... */
}
// 模块中根据环境选择实现
@Module({
providers: [
{
provide: "MAILER",
useClass: process.env.NODE_ENV === "production" ? SmtpMailer : MockMailer,
},
],
})
export class MailModule {}
3. 工厂提供者(useFactory)
动态创建实例,支持依赖其他提供者。工厂可以是异步的,返回 Promise。
@Module({
providers: [
{
provide: "DATABASE_CONNECTION",
useFactory: async (configService: ConfigService) => {
const dbConfig = configService.getDatabaseConfig();
const connection = await createDatabaseConnection(dbConfig);
return connection;
},
inject: [ConfigService], // 注入依赖,数组中顺序对应工厂函数参数
},
],
})
export class DatabaseModule {}
使用工厂时,inject 数组用于声明需要哪些提供者作为工厂参数。
4. 别名提供者(useExisting)
为已有的提供者创建一个别名,使得可以通过不同令牌注入同一个实例。
@Module({
providers: [SomeService, { provide: "ALIAS_SERVICE", useExisting: SomeService }],
})
export class MyModule {}
此时,'ALIAS_SERVICE' 和 SomeService 令牌指向同一个实例。
三、异步提供者
如果提供者的初始化需要异步操作(如数据库连接、HTTP 请求),可以使用 async 工厂函数或返回 Promise 的值提供者。
NestJS 会等待异步提供者解析完成,才注入到依赖该提供者的其他类中。
@Module({
providers: [
{
provide: "ASYNC_DB",
useFactory: async () => {
const connection = await orm.createConnection();
return connection;
},
},
],
})
export class AppModule {}
四、可选提供者(@Optional())
当某个依赖不是必须存在时,可以使用 @Optional() 装饰器。如果该提供者未注册,注入的值会是 undefined,而不会抛出错误。
@Injectable()
export class LoggerService {
constructor(@Optional() private readonly config?: ConfigService) {
if (config) {
// 使用 config 配置日志级别
} else {
// 使用默认配置
}
}
}
五、提供者的作用域
提供者默认是单例(SINGLETON)作用域,即整个应用共享同一个实例。但也可以通过 @Injectable({ scope: Scope.REQUEST }) 或 Scope.TRANSIENT 改变作用域。详细内容已在“模块系统与依赖注入”中介绍。
六、自定义提供者的令牌类型
提供者的 provide 令牌可以是:
- 字符串或 Symbol(如上例
'CONFIG') - 类本身(默认情况,
useClass或简写时) - 抽象类或接口(但在 JavaScript 中无法直接使用接口,需要借助字符串或 Symbol)
为了获得更好的类型安全,推荐使用自定义符号或类作为令牌。
export const DATABASE_CONNECTION = Symbol('DATABASE_CONNECTION');
// 在 provider 中使用
{ provide: DATABASE_CONNECTION, useFactory: ... }
// 注入时
@Inject(DATABASE_CONNECTION) private connection: Connection
总结
| 提供者类型 | 使用方式 | 适用场景 |
|---|---|---|
| 类提供者(服务) | 默认 @Injectable(),在 providers 中直接添加类 | 专注业务逻辑、可复用的服务 |
| 值提供者 | useValue | 常量、配置对象、第三方库实例 |
| 类提供者(替换) | useClass | 接口的不同实现(mock、生产环境) |
| 工厂提供者 | useFactory + inject | 需要依赖其他提供者创建实例 |
| 别名提供者 | useExisting | 同一实例不同令牌访问 |
| 异步提供者 | useFactory 返回 Promise | 数据库连接、初始化外部资源 |
| 可选提供者 | @Optional() 装饰器 | 依赖非必需的情况 |
理解这些提供者形式,可以灵活管理应用中的各种依赖,让 NestJS 应用更加模块化和可测试。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| NestJS 文档 | 官方 |
| Providers | 本章主题 |
| Request lifecycle | 执行顺序 |
相关文章
认证与授权
认证(Authentication):确认「你是谁」(如 JWT、Session)。 - 授权(Authorization):确认「你能做什么」(如 RBAC、策略检查)。
缓存
@nestjs/cache-manager 统一缓存 API,存储实现可插拔。
守卫 (Guards) 与授权
守卫决定是否放行请求,常用于认证与授权。见 auth。
配置管理(Config 模块)
@nestjs/config 加载 .env 并提供 ConfigService。见 工程化 env。
数据库集成(以 TypeORM 为例)
Nest 通过 @nestjs/typeorm 等包集成 ORM;生产环境用 migration,慎用 synchronize。亦可选用 Prisma、MikroORM 等(见 官方 Database)。
定时任务
@nestjs/schedule 基于 cron 表达式调度任务。
Series
new
18 / 19