配置管理(Config 模块)
@nestjs/config 加载 .env 并提供 ConfigService。见 工程化 env。
@nestjs/config 模块是 NestJS 官方提供的配置管理工具,它基于 dotenv,可以方便地加载环境变量、支持多环境配置、提供类型安全的配置服务,并可对环境变量进行验证。
一、@nestjs/config 模块
安装依赖:
npm install @nestjs/config
该模块导出了 ConfigModule 和 ConfigService。ConfigModule 需要导入到根模块(通常是 AppModule),以便在整个应用中使用 ConfigService。
二、加载环境变量:ConfigModule.forRoot()
最简单的用法是调用 ConfigModule.forRoot(),它会自动读取项目根目录下的 .env 文件,并将变量合并到 process.env 中。
// app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
@Module({
imports: [
ConfigModule.forRoot(), // 加载 .env 文件
],
})
export class AppModule {}
默认情况下,它会查找 .env 文件。如果文件不存在,则只读取系统环境变量。可以通过 envFilePath 自定义路径:
ConfigModule.forRoot({
envFilePath: ".env.development",
});
也可以指定多个文件(后面的会覆盖前面的):
ConfigModule.forRoot({
envFilePath: [".env.development.local", ".env.development"],
});
三、使用配置服务:ConfigService
ConfigService 提供了 get() 方法来获取配置变量。
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class AppService {
constructor(private configService: ConfigService) {}
getDatabaseHost(): string {
return this.configService.get<string>("DB_HOST");
}
}
get() 方法支持默认值:
const port = this.configService.get<number>("PORT", 3000);
ConfigService 还提供了 getOrThrow() 方法,如果变量不存在会抛出异常(适用于必需变量)。
const apiKey = this.configService.getOrThrow("API_KEY");
四、类型安全
@nestjs/config 本身不会自动生成类型,但可以使用 TypeScript 接口结合泛型来增强类型安全。
方案一:定义配置接口
// config.interface.ts
export interface EnvConfig {
DB_HOST: string;
DB_PORT: number;
NODE_ENV: "development" | "production" | "test";
API_KEY: string;
}
然后在服务中使用类型断言:
const dbHost = this.configService.get<string>('DB_HOST');
// 或封装成类型化的方法
get<T = any>(key: keyof EnvConfig): T {
return this.configService.get<T>(key);
}
方案二:使用 ConfigModule.forRoot 的 validationSchema 配合 Joi 生成类型
Joi 校验时可以自动推导类型,但仍需手动定义接口。
更好的做法:使用 @nestjs/config 的 ConfigModule.forRoot 的 load 选项加载自定义配置函数,该函数返回带有类型的对象,然后 ConfigService.get 会继承该类型(但仅适用于自定义配置节)。对于环境变量,通常还是需要手动断言。
五、验证环境变量
为了防止因缺少必要的环境变量导致应用异常,可以对环境变量进行验证。@nestjs/config 支持两种验证方式:
1. Joi 验证
安装 Joi:
npm install joi
在 ConfigModule.forRoot() 中传入 validationSchema:
import * as Joi from "joi";
@Module({
imports: [
ConfigModule.forRoot({
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid("development", "production", "test").default("development"),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().default(5432),
API_KEY: Joi.string().required(),
}),
}),
],
})
export class AppModule {}
如果环境变量不符合 schema,应用启动时会抛出错误。
2. class-validator 验证(配合自定义配置类)
虽然 @nestjs/config 不直接支持 class-validator,但可以通过自定义配置类 + 工厂函数实现:
// config.validation.ts
import { plainToClass } from "class-transformer";
import { validateSync } from "class-validator";
export class EnvVariables {
@IsString()
DB_HOST: string;
@IsNumber()
DB_PORT: number;
@IsString()
@IsIn(["development", "production", "test"])
NODE_ENV: string;
}
export function validate(config: Record<string, unknown>) {
const validatedConfig = plainToClass(EnvVariables, config, {
enableImplicitConversion: true,
});
const errors = validateSync(validatedConfig, { skipMissingProperties: false });
if (errors.length) {
throw new Error(`Config validation error: ${errors.toString()}`);
}
return validatedConfig;
}
然后在 ConfigModule.forRoot 中使用 validate 函数:
ConfigModule.forRoot({
validate,
});
六、多环境配置
通常开发、测试、生产环境需要使用不同的配置。可以通过 envFilePath 结合环境变量 NODE_ENV 来动态加载。
// app.module.ts
const envFilePath = `.env.${process.env.NODE_ENV || "development"}`;
@Module({
imports: [
ConfigModule.forRoot({
envFilePath,
isGlobal: true, // 使 ConfigModule 全局可用,避免在每个模块重复导入
}),
],
})
export class AppModule {}
也可以使用多个文件,让特定环境的配置覆盖默认配置:
ConfigModule.forRoot({
envFilePath: [".env", ".env.development"], // 后面的覆盖前面的
});
常见文件命名:
.env:通用配置(可提交).env.local:本地覆盖(不提交).env.development:开发环境.env.production:生产环境
七、自定义配置文件(load 属性)
除了环境变量,有时需要从 JSON 或 YAML 文件中加载配置。可以使用 load 属性,它接受一个返回配置对象的函数数组。
import { readFileSync } from "fs";
import * as yaml from "js-yaml";
const loadYamlConfig = () => {
const configFile = process.env.CONFIG_FILE || "./config.yaml";
return yaml.load(readFileSync(configFile, "utf8")) as Record<string, any>;
};
@Module({
imports: [
ConfigModule.forRoot({
load: [loadYamlConfig],
}),
],
})
export class AppModule {}
load 中的函数可以返回对象,该对象会合并到最终的配置中,通过 ConfigService.get('key') 访问。注意,若环境变量和自定义配置有相同 key,环境变量优先级更高(取决于 ConfigModule 的配置顺序,通常 load 的配置会先加载,然后被环境变量覆盖)。
实际应用:
- 敏感信息(数据库密码、API 密钥)放在环境变量中。
- 静态配置(如功能开关、业务规则)放在 YAML/JSON 配置文件中。
八、全局使用 ConfigModule
为了避免在每个模块中重复导入 ConfigModule,可以将其设置为全局模块:
ConfigModule.forRoot({
isGlobal: true,
});
这样其他模块中无需再导入 ConfigModule 即可注入 ConfigService。
九、最佳实践
- 定义配置接口:为环境变量和自定义配置维护 TypeScript 接口,并在读取时使用类型断言。
- 验证环境变量:在应用启动时使用 Joi 或 class-validator 进行验证,确保必需变量存在且格式正确。
- 分离敏感信息:不要将
.env文件提交到版本控制,使用.env.example给出模板。 - 使用默认值:为可选配置提供合理的默认值,避免因缺失变量导致服务启动失败。
- 生产环境禁用
synchronize等危险选项:通过环境变量控制。
十、示例:完整的配置模块
# .env.example
DB_HOST=localhost
DB_PORT=5432
NODE_ENV=development
// app.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import * as Joi from "joi";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`,
validationSchema: Joi.object({
NODE_ENV: Joi.string().valid("development", "production", "test").required(),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().default(5432),
}),
}),
],
})
export class AppModule {
constructor(private configService: ConfigService) {
// 使用配置
const dbHost = this.configService.get<string>("DB_HOST");
console.log(`Database host: ${dbHost}`);
}
}
总结
| 功能 | 方法 |
|---|---|
| 加载环境变量 | ConfigModule.forRoot() |
| 读取配置 | ConfigService.get() / getOrThrow() |
| 类型安全 | 自定义接口 + 类型断言 |
| 验证 | Joi schema 或 class-validator |
| 多环境 | 通过 envFilePath 动态指定文件 |
| 自定义配置 | load 属性加载 YAML/JSON 文件 |
| 全局可用 | isGlobal: true |
使用 @nestjs/config 可以系统地管理配置,使应用在不同环境下保持行为一致且安全。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| NestJS 文档 | 官方 |
| Configuration | 本章主题 |
| Request lifecycle | 执行顺序 |
相关文章
认证与授权
认证(Authentication):确认「你是谁」(如 JWT、Session)。 - 授权(Authorization):确认「你能做什么」(如 RBAC、策略检查)。
缓存
@nestjs/cache-manager 统一缓存 API,存储实现可插拔。
提供者与服务
Service 是最常见的 Provider,封装业务逻辑。见 module。
守卫 (Guards) 与授权
守卫决定是否放行请求,常用于认证与授权。见 auth。
数据库集成(以 TypeORM 为例)
Nest 通过 @nestjs/typeorm 等包集成 ORM;生产环境用 migration,慎用 synchronize。亦可选用 Prisma、MikroORM 等(见 官方 Database)。
定时任务
@nestjs/schedule 基于 cron 表达式调度任务。