NestJS 核心架构
模块化设计、依赖注入与装饰器模式
目录
模块化设计
核心概念
┌─────────────────────────────────────────────────────────┐
│ Module │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ Controllers │ ←──│ Providers │←───│ Imports │ │
│ └─────────────┘ └─────────────┘ └──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ 处理 HTTP 请求 业务逻辑/服务 │
│ │
│ ┌─────────────┐ │
│ │ Exports │ → 暴露给其他模块使用 │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘模块定义
typescript
// users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { DatabaseModule } from '../database/database.module';
@Module({
imports: [DatabaseModule], // 导入其他模块
controllers: [UsersController], // 该模块的控制器
providers: [UsersService], // 该模块的服务提供者
exports: [UsersService] // 暴露给外部模块
})
export class UsersModule {}Controller 与 Service
typescript
// users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
@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: CreateUserDto) {
return this.usersService.create(createUserDto);
}
}
// users.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UsersService {
private users = [];
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find(u => u.id === id);
}
create(data: CreateUserDto) {
const user = { id: Date.now(), ...data };
this.users.push(user);
return user;
}
}动态模块
typescript
// config.module.ts
import { Module, DynamicModule, Global } from '@nestjs/common';
@Global()
@Module({})
export class ConfigModule {
static forRoot(options: ConfigOptions): DynamicModule {
return {
module: ConfigModule,
providers: [
{
provide: 'CONFIG_OPTIONS',
useValue: options
},
ConfigService
],
exports: [ConfigService]
};
}
static forRootAsync(options: ConfigAsyncOptions): DynamicModule {
return {
module: ConfigModule,
imports: options.imports || [],
providers: [
{
provide: 'CONFIG_OPTIONS',
useFactory: options.useFactory,
inject: options.inject || []
},
ConfigService
],
exports: [ConfigService]
};
}
}
// 使用
@Module({
imports: [
ConfigModule.forRootAsync({
imports: [SecretsModule],
inject: [SecretsService],
useFactory: (secrets: SecretsService) => ({
apiKey: secrets.get('API_KEY')
})
})
]
})
export class AppModule {}依赖注入
Provider 类型
typescript
// 标准类 Provider
@Injectable()
export class UsersService {}
// 值 Provider
{
provide: 'API_KEY',
useValue: 'my-secret-key'
}
// 工厂 Provider
{
provide: 'ASYNC_CONNECTION',
useFactory: async (config: ConfigService) => {
const connection = await createConnection(config.get('DB_URL'));
return connection;
},
inject: [ConfigService]
}
// 类 Provider(别名)
{
provide: 'AbstractLogger',
useClass: process.env.NODE_ENV === 'production'
? ProductionLogger
: DevelopmentLogger
}
// 已存在 Provider(别名)
{
provide: 'AliasService',
useExisting: UsersService
}作用域
typescript
import { Injectable, Scope } from '@nestjs/common';
// 单例(默认)
@Injectable()
export class SingletonService {}
// 请求级别
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {}
// 瞬态(每次注入都创建新实例)
@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {}循环依赖处理
typescript
// a.service.ts
@Injectable()
export class AService {
constructor(
@Inject(forwardRef(() => BService))
private bService: BService
) {}
}
// b.service.ts
@Injectable()
export class BService {
constructor(
@Inject(forwardRef(() => AService))
private aService: AService
) {}
}装饰器原理
装饰器本质
typescript
// TypeScript 装饰器 = 函数
function Injectable(): ClassDecorator {
return (target: Function) => {
// 存储元数据
Reflect.defineMetadata('injectable', true, target);
};
}
// 获取元数据
const isInjectable = Reflect.getMetadata('injectable', SomeClass);自定义装饰器
typescript
// 参数装饰器
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
}
);
// 使用
@Get('profile')
getProfile(@User() user: UserEntity) {
return user;
}
@Get('profile')
getUsername(@User('username') username: string) {
return { username };
}typescript
// 方法装饰器
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
// 使用
@Post()
@Roles('admin')
create() {}
// 在 Guard 中读取
const roles = this.reflector.get<string[]>('roles', context.getHandler());typescript
// 组合装饰器
import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';
export function Auth(...roles: string[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(AuthGuard, RolesGuard),
ApiBearerAuth(),
ApiUnauthorizedResponse({ description: 'Unauthorized' })
);
}
// 使用
@Post()
@Auth('admin')
create() {}请求生命周期
收到请求
│
▼
┌───────────────┐
│ Middleware │ 可访问 req/res
└───────┬───────┘
▼
┌───────────────┐
│ Guards │ 决定是否放行
└───────┬───────┘
▼
┌───────────────┐
│ Interceptors │ 前置逻辑
│ (before) │
└───────┬───────┘
▼
┌───────────────┐
│ Pipes │ 参数转换/验证
└───────┬───────┘
▼
┌───────────────┐
│ Controller │ 处理请求
│ Handler │
└───────┬───────┘
▼
┌───────────────┐
│ Interceptors │ 后置逻辑
│ (after) │
└───────┬───────┘
▼
┌───────────────┐
│Exception Filter│ 异常处理
└───────┬───────┘
▼
响应发送高频面试题
Q1: NestJS 的依赖注入是如何实现的?
- 装饰器标记:
@Injectable()标记可注入类 - 元数据反射:
reflect-metadata存储构造函数参数类型 - IoC 容器:扫描模块,创建并缓存实例
- 懒加载:按需实例化依赖
typescript
// 简化原理
function resolve(token) {
if (container.has(token)) {
return container.get(token);
}
const dependencies = Reflect.getMetadata('design:paramtypes', token);
const instances = dependencies.map(dep => resolve(dep));
const instance = new token(...instances);
container.set(token, instance);
return instance;
}Q2: Module 的 forRoot 和 forRootAsync 区别?
| 方法 | 配置方式 | 适用场景 |
|---|---|---|
forRoot | 同步配置 | 静态配置值 |
forRootAsync | 异步配置 | 需要注入其他服务 |
Q3: 如何在 NestJS 中处理循环依赖?
- forwardRef:延迟解析依赖
- 重构:提取公共逻辑到第三个服务
- 模块重新分层:调整模块依赖关系
Q4: Provider 的作用域有哪些?
| 作用域 | 说明 | 使用场景 |
|---|---|---|
DEFAULT (Singleton) | 应用生命周期单例 | 无状态服务 |
REQUEST | 每个请求创建新实例 | 请求级上下文 |
TRANSIENT | 每次注入创建新实例 | 无共享状态 |
项目结构推荐
src/
├── app.module.ts
├── main.ts
├── common/
│ ├── decorators/
│ ├── filters/
│ ├── guards/
│ ├── interceptors/
│ └── pipes/
├── config/
│ ├── config.module.ts
│ └── config.service.ts
├── users/
│ ├── users.module.ts
│ ├── users.controller.ts
│ ├── users.service.ts
│ ├── dto/
│ │ └── create-user.dto.ts
│ └── entities/
│ └── user.entity.ts
└── database/
├── database.module.ts
└── database.providers.ts