以下是 NestJS 配置管理完整方案 + 代码汇总,覆盖四大配置方案、两种依赖注入模式、三种验证体系,所有代码均可直接落地使用。
一、四大配置方案(含完整实现代码) 方案 1:原生 dotenv 方案(底层基础)
适用场景:纯 Node.js 简易工具、极简原型项目,不依赖 Nest 框架。
核心原理:零依赖库,直接将 .env 文件键值对注入 process.env。
arduino
1. 安装依赖
bash
运行
npm i dotenv
2. 根目录创建 .env
env
TOKEN_SECRET=your-long-random-secret
DB_USER=admin
DB_PASSWORD=example123
3. 入口文件使用
javascript
运行
// index.js
require('dotenv').config();
console.log(process.env.DB_USER); // admin
console.log(process.env.TOKEN_SECRET); // your-long-random-secret
方案 2:官方 @nestjs/config + 多环境 .env(主流首选)
适用场景:绝大多数中小型 NestJS 项目、容器化 / 云原生部署,是官方推荐的标准方案。
核心特性:多环境文件分层合并、全局注入、配合枚举消除魔法字符串。
css
1. 安装依赖
bash
运行
npm i @nestjs/config dotenv
npm i -D cross-env
ini
2. 创建多环境配置文件
根目录下创建三层配置文件,遵循「公共兜底 + 环境覆盖」原则:
env
# .env 公共兜底配置
APP_NAME=nest-admin
DB_URL=https://default.example.com
env
# .env.development 开发环境
DB=mysql-dev
DB_HOST=127.0.0.1
DB_PORT=3306
env
# .env.production 生产环境
DB=mysql-prod
DB_HOST=10.0.0.1
DB_PORT=3306
ini
3. 枚举统一管理配置键(避免魔法字符串)
typescript
运行
// src/enum/config.const.ts
export enum ConfigEnum {
DB = 'DB',
DB_HOST = 'DB_HOST',
DB_PORT = 'DB_PORT',
DB_URL = 'DB_URL',
PORT = 'PORT',
}
typescript
4. 全局注册配置模块
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as dotenv from 'dotenv';
import { UserModule } from './user/user.module';
// 动态生成环境配置文件路径
const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // 全局生效,所有模块无需重复导入
envFilePath, // 加载当前环境的差异化配置
// 加载公共兜底配置,环境配置会覆盖同名变量
load: [() => dotenv.config({ path: '.env' })],
}),
UserModule,
],
})
export class AppModule {}
go
5. 配置启动脚本(package.json)
json
{
"scripts": {
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:prod": "cross-env NODE_ENV=production node dist/main"
}
}
kotlin
6. 业务代码中使用
typescript
运行
// src/user/user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from '../enum/config.const';
@Controller('user')
export class UserController {
constructor(private configService: ConfigService) {}
@Get()
getConfig() {
const dbType = this.configService.get(ConfigEnum.DB);
const dbHost = this.configService.get(ConfigEnum.DB_HOST);
const dbUrl = this.configService.get(ConfigEnum.DB_URL);
return { dbType, dbHost, dbUrl };
}
}
方案 3:YAML 分层配置方案(复杂配置场景)
适用场景:配置层级深、数量多的中大型项目,YAML 格式对嵌套结构可读性更强。
核心原理:js-yaml 解析文件 + lodash.merge 深度合并公共配置与环境配置。
css
1. 安装依赖
bash
运行
npm i js-yaml lodash
npm i -D @types/js-yaml @types/lodash
yaml
2. 创建 YAML 配置文件
在根目录 config 文件夹下创建三个文件:
yaml
# config/config.yml 公共基础配置
db:
mysql1:
host: 127.0.0.1
port: 3306
name: default-db
mysql2:
host: 127.0.0.1
port: 3306
name: default-db2
yaml
# config/config.development.yml 开发环境覆盖
db:
mysql1:
name: mysql-dev
mysql2:
name: mysql-dev2
yaml
# config/config.production.yml 生产环境覆盖
db:
mysql1:
name: mysql-prod
mysql2:
name: mysql-prod2
javascript
3. 编写配置加载函数
typescript
运行
// src/config/configuration.ts
import { readFileSync } from 'fs';
import * as yaml from 'js-yaml';
import { join } from 'path';
import { merge } from 'lodash';
// 公共配置文件路径
const commonFilePath = join(__dirname, '../../config/config.yml');
// 环境配置文件路径
const envFilePath = join(
__dirname,
'../../config',
`config.${process.env.NODE_ENV || 'development'}.yml`
);
// 解析并合并配置
const commonConfig = yaml.load(readFileSync(commonFilePath, 'utf8'));
const envConfig = yaml.load(readFileSync(envFilePath, 'utf8'));
// 导出工厂函数,环境配置会深度覆盖公共配置
export default () => {
return merge(commonConfig, envConfig);
};
typescript
4. 注册到配置模块
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configuration], // 加载自定义 YAML 配置
}),
],
})
export class AppModule {}
kotlin
5. 读取嵌套配置
typescript
运行
// 支持点语法读取嵌套属性
const mysql1Config = this.configService.get('db.mysql1');
// { host: '127.0.0.1', port: 3306, name: 'mysql-dev' }
方案 4:node-config 方案(独立配置体系)
适用场景:偏好约定式配置、不依赖 Nest 注入体系的项目,原生支持多环境自动合并。
核心特性:按文件名约定自动加载对应环境配置,支持 JSON/YAML 双格式。
css
1. 安装依赖
bash
运行
npm i config cross-env
npm i -D @types/config
arduino
2. 创建配置文件
在根目录 config 文件夹下创建:
json
// config/default.json 公共默认配置
{
"server": {
"appName": "nest-admin"
},
"db": {
"host": "localhost",
"port": 3306
}
}
json
// config/development.json 开发环境
{
"db": {
"username": "dev_user",
"password": "dev123"
}
}
json
// config/production.json 生产环境
{
"db": {
"username": "prod_user",
"password": "prod@2024"
}
}
csharp
3. 直接读取使用
typescript
运行
import * as config from 'config';
// 读取嵌套配置
const dbConfig = config.get('db');
const dbHost = config.get<string>('db.host');
json
4. 启动脚本
json
{
"scripts": {
"dev": "cross-env NODE_ENV=development node index.js",
"prod": "cross-env NODE_ENV=production node index.js"
}
}
补充:该库也支持 YAML 格式,只需将文件后缀改为 .yml 即可,使用方式完全一致。
二、进阶:依赖注入式配置管理(含 Inject 实现)
模式 1:registerAs 命名空间 + @Inject 注入
适用场景:中大型项目,按业务域拆分配置,按需精准注入,符合依赖注入设计原则。
arduino
1. 按模块定义命名空间配置
typescript
运行
// src/config/database.config.ts
import { registerAs } from '@nestjs/config';
// 第一个参数:命名空间名称;第二个参数:配置工厂函数
export const databaseConfig = registerAs('database', () => ({
host: process.env.DATABASE_HOST || 'localhost',
port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
username: process.env.DATABASE_USER || 'postgres',
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_NAME || 'app_db',
}));
typescript
运行
// src/config/app.config.ts
import { registerAs } from '@nestjs/config';
export const appConfig = registerAs('app', () => ({
port: parseInt(process.env.PORT, 10) || 3000,
env: process.env.NODE_ENV || 'development',
apiPrefix: process.env.API_PREFIX || '/api',
}));
typescript
2. 全局注册命名空间
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { databaseConfig } from './config/database.config';
import { appConfig } from './config/app.config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env', `.env.${process.env.NODE_ENV || 'development'}`],
load: [databaseConfig, appConfig], // 注册所有命名空间
}),
],
})
export class AppModule {}
typescript
3. 服务中通过 @Inject 精准注入
typescript
运行
// src/database/database.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { databaseConfig } from '../config/database.config';
import type { ConfigType } from '@nestjs/config';
@Injectable()
export class DatabaseService {
constructor(
// 注入 database 命名空间的完整配置对象
@Inject(databaseConfig.KEY)
private dbConfig: ConfigType<typeof databaseConfig>,
) {}
getConnection() {
// 完整类型提示,无需手动声明接口
return {
host: this.dbConfig.host,
port: this.dbConfig.port,
user: this.dbConfig.username,
};
}
}
模式 2:forRootAsync + inject 工厂异步注入
适用场景:远程配置中心拉取、配置解密、依赖其他服务的动态配置场景。
typescript
同步工厂 inject 示例
typescript
运行
// src/app.module.ts
import { Module, Logger } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
// 配置工厂函数,参数为注入的依赖
const configurationFactory = (logger: Logger) => {
logger.log('加载应用配置...', 'ConfigFactory');
return {
app: { port: parseInt(process.env.PORT, 10) || 3000 },
database: { host: process.env.DB_HOST },
};
};
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [configurationFactory],
inject: [Logger], // 声明依赖,顺序与工厂函数参数一一对应
}),
],
providers: [Logger],
})
export class AppModule {}
typescript
异步工厂 useFactory 示例(远程配置场景)
typescript
运行
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { HttpModule, HttpService } from '@nestjs/axios';
@Module({
imports: [
HttpModule,
ConfigModule.forRootAsync({
isGlobal: true,
inject: [HttpService], // 注入依赖服务
useFactory: async (httpService: HttpService) => {
// 异步拉取远程配置中心配置
const { data: remoteConfig } = await httpService.axiosRef.get(
'https://config-center.example.com/app-config'
);
// 合并本地环境变量与远程配置
return {
...remoteConfig,
local: { env: process.env.NODE_ENV },
};
},
}),
],
})
export class AppModule {}
三、配置验证三大体系(含完整实现代码)
方案 1:Joi 验证(经典成熟方案)
适用场景:JavaScript 项目、存量项目,校验规则丰富度要求高。
css
1. 安装依赖
bash
运行
npm i joi
kotlin
2. 模块中配置校验规则
typescript
运行
// src/app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';
const envFilePath = `.env.${process.env.NODE_ENV || 'development'}`;
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath,
// Joi 校验 Schema
validationSchema: Joi.object({
NODE_ENV: Joi.string()
.valid('development', 'production', 'test')
.default('development'),
PORT: Joi.number().default(3000),
DB_HOST: Joi.string().ip().required(),
DB_PORT: Joi.number().port().default(3306),
DB_URL: Joi.string().domain(),
}),
validationOptions: {
allowUnknown: false, // 禁止未定义的环境变量
abortEarly: true, // 遇到第一个错误即终止
},
}),
],
})
export class AppModule {} //配置不合法时,应用启动阶段直接抛出错误并终止,避免线上运行时故障。
方案 2:class-validator 验证(装饰器风格)
适用场景:团队技术栈统一使用装饰器风格,与接口 DTO 校验保持一致体验。
arduino
1. 安装依赖
bash
运行
npm i class-validator class-transformer
typescript
2. 定义校验类与验证函数
typescript
运行
// src/config/env.validation.ts
import { plainToInstance } from 'class-transformer';
import { IsEnum, IsNumber, IsString, IsIP, validateSync } from 'class-validator';
enum Environment {
Development = 'development',
Production = 'production',
Test = 'test',
}
class EnvironmentVariables {
@IsEnum(Environment)
NODE_ENV: Environment;
@IsNumber()
PORT: number;
@IsIP()
DB_HOST: string;
@IsString()
DB_USER: string;
}
export function validate(config: Record<string, unknown>) {
const validatedConfig = plainToInstance(EnvironmentVariables, config, {
enableImplicitConversion: true, // 自动类型转换
});
const errors = validateSync(validatedConfig, {
skipMissingProperties: false,
});
if (errors.length > 0) {
throw new Error(`配置验证失败: ${errors.toString()}`);
}
return validatedConfig;
}
javascript
3. 模块中注册
typescript
运行
import { validate } from './config/env.validation';
ConfigModule.forRoot({
isGlobal: true,
validate, // 注册自定义验证函数
});
方案 3:Zod 验证(TypeScript 首选)
适用场景:全新 TypeScript 项目,一次定义同时获得运行时校验与静态类型,开发体验最佳。
css
1. 安装依赖
bash
运行
npm i zod
css
2. 定义 Schema 并自动推导类型
typescript
运行
// src/config/env.schema.ts
import { z } from 'zod';
export const envSchema = z.object({
NODE_ENV: z
.enum(['development', 'production', 'test'])
.default('development'),
PORT: z.coerce.number().default(3000), // 自动字符串转数字
DATABASE_HOST: z.string().min(1),
DATABASE_PORT: z.coerce.number().int().positive(),
DATABASE_USER: z.string().min(1),
DATABASE_PASSWORD: z.string().min(6),
});
// 自动推导 TypeScript 类型,无需重复编写 interface
export type EnvType = z.infer<typeof envSchema>;
typescript
3. 编写验证函数
typescript
运行
// src/config/validate.ts
import { envSchema } from './env.schema';
export function validate(config: Record<string, unknown>) {
const result = envSchema.safeParse(config);
if (!result.success) {
// 格式化错误信息,清晰展示所有不合法项
const errorList = result.error.issues.map(
(item) => `[${item.path.join('.')}] ${item.message}`
);
throw new Error(`配置验证失败:\n${errorList.join('\n')}`);
}
return result.data;
}
javascript
4. 模块注册 + 类型化使用
typescript
运行
// src/app.module.ts
import { validate } from './config/validate';
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env', `.env.${process.env.NODE_ENV || 'development'}`],
validate,
});
kotlin
typescript
运行
// 业务代码中获得完整类型提示
import { ConfigService } from '@nestjs/config';
import type { EnvType } from '../config/env.schema';
@Injectable()
export class AppService {
// 第二个泛型设为 true,所有键均非 undefined
constructor(private configService: ConfigService<EnvType, true>) {}
getDbConfig() {
// 自动补全键名,返回值类型精准推导
const host = this.configService.get('DATABASE_HOST'); // string
const port = this.configService.get('DATABASE_PORT'); // number
return { host, port };
}
}
四、选型速查与最佳实践
| 项目规模 | 推荐组合 |
|---|---|
| 快速原型 / 小型工具 | @nestjs/config + 单 .env 文件 |
| 标准业务项目 | @nestjs/config + 多环境 .env + Zod/Joi 验证 |
| 中大型复杂项目 | registerAs 命名空间拆分 + Zod 类型校验 + YAML/.env 分层 |
| 企业级配置中心 | forRootAsync 异步工厂 + 远程配置拉取 + 全量校验 |
核心最佳实践
- 优先官方体系:以 @nestjs/config 为核心,最大化复用 Nest 生态的注入、验证、模块化能力。
- 配置键常量化:通过枚举统一管理键名,消除硬编码魔法字符串,降低维护成本。
- 分层配置原则:公共默认配置放基础文件,环境差异化配置放对应环境文件,遵循「兜底 + 覆盖」逻辑。
- 强制启动校验:生产环境必须开启配置验证,在启动阶段拦截配置错误。
- TS 项目优先 Zod:一次定义实现静态类型与运行时校验双重保障,减少重复代码。
- 跨平台兼容:使用 cross-env 统一设置环境变量,规避系统差异。
- 敏感配置外置:密钥、密码不提交代码仓库,通过部署平台或配置中心注入。