在中大型 NestJS 项目中,配置管理是系统稳定运行的关键一环。
如何优雅地加载 .env 文件?如何实现类型安全校验?如何区分开发、测试、生产环境的配置?
本篇将完整分享我们在项目中如何构建一个清晰、可靠、可扩展的配置系统。
1. 为什么配置管理很重要?
.env配置混乱,环境变量被硬编码- 多环境切换不清晰,部署频繁出错
- 缺少校验,配置缺失直到运行时报错
- 多个模块依赖配置,导入重复、维护困难
我们通过 @nestjs/config 模块 + 配置加载器(Configuration Loader)+ 类型校验,构建了一套 可组合、可校验、易扩展 的配置方案。
2. 配置 ConfigService
1. 安装依赖
pnpm add @nestjs/config joi
2. 项目结构
src/
├── config/
│ ├── configuration.ts # 配置加载器
│ ├── validation.ts # Joi 校验规则
│ └── config.interface.ts # 类型定义(可选)
├── app.module.ts
├── main.ts
├── .env
├── .env.development
├── .env.production
├── .env.test
3. 配置加载器 configuration.ts
集中管理 .env 中的配置项,便于类型补全与模块间复用。
// src/config/configuration.ts
export default () => ({
port: parseInt(process.env.PORT ?? '3000', 10),
database: {
uri: process.env.MONGODB_URI,
},
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN ?? '1d',
},
});
4. 配置校验 validation.ts
使用 Joi 在应用启动前强校验配置项合法性,防止运行时才暴露错误。
// src/config/validation.ts
import * as Joi from 'joi';
export const validationSchema = Joi.object({
PORT: Joi.number().default(3000),
MONGODB_URI: Joi.string().uri().required(),
JWT_SECRET: Joi.string().required(),
JWT_EXPIRES_IN: Joi.string().default('1d'),
});
5. AppModule 中引入配置模块
将配置模块设置为全局,并支持多环境 .env 文件加载。
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
import { validationSchema } from './config/validation';
const getEnvFilePath = (): string[] => {
const env = process.env.NODE_ENV;
const base = '.env';
const mapping: Record<string, string> = {
production: '.env.production',
development: '.env.development',
test: '.env.test',
};
return [mapping[env ?? 'development'], base];
};
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configuration],
validationSchema,
envFilePath: getEnvFilePath(),
}),
// ...其他模块
],
})
export class AppModule {}
3. 使用配置 ConfigService
在任何服务或模块中使用 ConfigService 获取类型安全的配置。
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class AuthService {
constructor(private readonly configService: ConfigService) {}
getJwtSecret(): string {
return this.configService.get<string>('jwt.secret');
}
}
启动项目时使用正确的环境变量
NODE_ENV=production node dist/main.js
4. Kubernetes 注入环境变量
生产环境使用 Kubernetes 注入的环境变量, 禁止 .env.production 文件被打包进 Docker 镜像。
1. 修改 getEnvFilePath
const getEnvFilePath = (): string[] => {
const env = process.env.NODE_ENV;
const base = '.env';
const mapping: Record<string, string> = {
development: '.env.development',
test: '.env.test',
};
return env === 'production' ? undefined : [mapping[env ?? 'development'], base];
};
2. K8s 配置
-
configmap
# configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: myapp-config data: PORT: "3000" DATABASE_URL: "postgres://user:pass@host:5432/db" -
deployment
# deployment.yaml spec: containers: - name: myapp image: your-image:tag envFrom: - configMapRef: name: myapp-config
如果你也在用 NestJS 构建中后台项目,不妨参考这套方式,统一你的配置管理逻辑,提升代码的健壮性与可维护性。