NestJS多环境配置进阶学习笔记
NestJS多环境配置进阶学习笔记
核心知识点
1. TypeScript 源码导航技巧
1.1 快速查看定义
在 VSCode 中,按住 Command(Mac)或 Control(Windows)键,点击代码中的模块、方法或属性,即可跳转到其类型定义文件。
1.2 通过注释了解 API
跳转到定义后,通过**官方文档注释(JSDoc)**即可快速了解该属性/方法的作用和支持的参数:
// 点击 ConfigModule.forRoot 跳转到定义后可见:
interface ConfigModuleOptions {
isGlobal?: boolean; // "If true, registers ConfigModule as a global module"
ignoreEnvFile?: boolean; // "If true, it will ignore the .env file"
envFilePath?: string; // "Custom path to the .env file"
load?: ConfigFactory[]; // Custom config loading functions
// ...
}技巧:善用 TypeScript 的类型跳转,可以在不查文档的情况下快速了解 API 用法。
2. envFilePath 指定环境文件
2.1 ConfigModule.forRoot 可选属性
通过查看源码定义,ConfigModule.forRoot() 支持以下关键属性:
| 属性 | 类型 | 作用 |
|---|---|---|
isGlobal | boolean | 是否为全局模块 |
ignoreEnvFile | boolean | 是否忽略 .env 文件 |
envFilePath | string | 指定 .env 文件路径 |
load | ConfigFactory[] | 自定义配置加载函数数组 |
2.2 创建多环境文件
project-root/
├── .env # 公共共享配置
├── .env.development # 开发环境专属配置
├── .env.production # 生产环境专属配置
└── src/
└── app.module.ts.env.development:
DB=mysql_dev.env.production:
DB=mysql_pro2.3 动态指定 envFilePath
使用 process.env.NODE_ENV 动态决定加载哪个环境文件:
// app.module.ts
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
// 动态拼接文件路径:.env.development 或 .env.production
envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
}),
],
})
export class AppModule {}2.4 使用 cross-env 设置环境变量
安装跨平台环境变量工具:
pnpm install cross-env -D在 package.json 中配置脚本:
{
"scripts": {
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:pro": "cross-env NODE_ENV=production node dist/main"
}
}
cross-env:解决 Windows 和 Mac/Linux 设置环境变量命令不兼容的问题。Windows 用set NODE_ENV=xxx,Mac/Linux 用export NODE_ENV=xxx,cross-env统一了写法。
2.5 运行效果
# 开发环境
pnpm start:dev
# → 加载 .env.development → DB = 'mysql_dev'
# 生产环境
pnpm start:pro
# → 加载 .env.production → DB = 'mysql_pro'3. envFilePath 的局限性
3.1 存在的问题
当项目配置项较多时,.env.development 和 .env.production 中会存在大量重复配置:
# .env.development
DB=mysql_dev
DB_HOST=127.0.0.1 # ← 与 production 重复
DB_PORT=3306 # ← 与 production 重复
DB_USERNAME=root # ← 与 production 重复
DB_URL=https://xxx.com # ← 与 production 重复
# .env.production
DB=mysql_pro
DB_HOST=127.0.0.1 # ← 重复!
DB_PORT=3306 # ← 重复!
DB_USERNAME=root # ← 重复!
DB_URL=https://xxx.com # ← 重复!痛点:
- 新增公共配置时,需要同时在两个文件中添加,容易遗漏
- 修改公共配置时,需要同时修改两个文件,维护成本高
.env文件与.env.development/.env.production之间没有关联关系
3.2 解决思路
需要一个共享配置文件(.env),存放两个环境都需要的公共配置,各环境文件只存放差异配置。
4. load 属性 + dotenv 实现共享配置
4.1 核心原理
ConfigModule.forRoot() 的 load 属性接收一个函数数组,每个函数返回一个键值对对象。该对象中的值会合并到配置中。
load?: ConfigFactory[];
// ConfigFactory = () => Record<string, any>4.2 安装 dotenv
pnpm install dotenv4.3 配置共享文件
.env(公共配置):
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_URL=https://3w.mock.com.env.development(开发环境差异):
DB=mysql_dev
DB_URL=https://3w.mock1.com.env.production(生产环境差异):
DB=mysql_pro4.4 在 AppModule 中配置 load
// app.module.ts
import { ConfigModule } from '@nestjs/config';
import * as dotenv from 'dotenv';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
// 加载 .env 公共共享配置
load: [
() => dotenv.config({ path: '.env' }),
],
}),
],
})
export class AppModule {}4.5 配置合并与覆盖规则
优先级(从高到低):
.env.development / .env.production → 覆盖
.env(通过 load 加载) → 被覆盖| 配置项 | .env(共享) | .env.development | 最终值 |
|---|---|---|---|
DB | - | mysql_dev | mysql_dev |
DB_HOST | 127.0.0.1 | - | 127.0.0.1 |
DB_PORT | 3306 | - | 3306 |
DB_URL | https://3w.mock.com | https://3w.mock1.com | https://3w.mock1.com 被覆盖 |
关键规则:环境专属文件的配置会覆盖共享文件中的同名配置项。
5. 完整配置文件结构
project-root/
├── .env # 公共共享配置(host、port、username 等)
├── .env.development # 开发环境差异配置
├── .env.production # 生产环境差异配置
├── .gitignore # 忽略 .env* 文件
├── package.json
└── src/
├── app.module.ts # ConfigModule 注册
├── enum/
│ └── config.enum.ts # 配置键名枚举
└── user/
├── user.module.ts
└── user.controller.ts # ConfigService 使用代码实战案例
需求描述
构建一个支持多环境的 NestJS 配置系统,实现 .env 共享配置 + .env.development / .env.production 差异配置的自动合并。
完整实现
第一步:安装依赖
pnpm install @nestjs/config dotenv cross-env -D第二步:创建配置文件
.env(公共配置):
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USERNAME=root
DB_URL=https://3w.mock.com.env.development(开发环境差异):
DB=mysql_dev
DB_URL=https://dev.mock.com.env.production(生产环境差异):
DB=mysql_pro
DB_URL=https://prod.mock.com第三步:配置 AppModule
// src/app.module.ts
import { ConfigModule } from '@nestjs/config';
import { Module } from '@nestjs/common';
import * as dotenv from 'dotenv';
import { UserModule } from './user/user.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
// 动态加载对应环境的 .env 文件
envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
// 加载公共共享配置
load: [() => dotenv.config({ path: '.env' })],
}),
UserModule,
],
})
export class AppModule {}第四步:定义枚举并使用
// src/enum/config.enum.ts
export enum ConfigEnum {
DB = 'DB',
DB_HOST = 'DB_HOST',
DB_PORT = 'DB_PORT',
DB_USERNAME = 'DB_USERNAME',
DB_URL = 'DB_URL',
}// src/user/user.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ConfigEnum } from '../enum/config.enum';
@Controller('user')
export class UserController {
constructor(private configService: ConfigService) {}
@Get()
getUsers() {
console.log('DB 类型:', this.configService.get(ConfigEnum.DB));
console.log('DB 地址:', this.configService.get(ConfigEnum.DB_HOST));
console.log('DB 端口:', this.configService.get(ConfigEnum.DB_PORT));
console.log('测试 URL:', this.configService.get(ConfigEnum.DB_URL));
return { users: [] };
}
}第五步:配置脚本并测试
// package.json
{
"scripts": {
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:pro": "cross-env NODE_ENV=production node dist/main"
}
}测试结果:
# 开发环境
pnpm start:dev
# → DB: mysql_dev
# → DB_HOST: 127.0.0.1(来自共享 .env)
# → DB_PORT: 3306(来自共享 .env)
# → DB_URL: https://dev.mock.com(development 覆盖了 .env)
# 生产环境
pnpm start:pro
# → DB: mysql_pro
# → DB_HOST: 127.0.0.1(来自共享 .env)
# → DB_PORT: 3306(来自共享 .env)
# → DB_URL: https://prod.mock.com(production 覆盖了 .env)常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
envFilePath 设置后 .env 不生效 | envFilePath 指向了其他文件,默认 .env 被忽略 | 使用 load 属性手动加载 .env 共享文件 |
| 环境专属配置未覆盖共享配置 | 配置项键名不一致 | 确保两个文件中键名完全相同 |
| Windows 环境变量设置失败 | export 命令不兼容 Windows | 使用 cross-env 统一设置 |
修改 .env 后配置未更新 | 需要重启应用 | Ctrl+C 后重新启动 |
不确定 forRoot 支持哪些属性 | - | Cmd/Ctrl + Click 跳转到类型定义查看 |
学习要点总结
- TypeScript 类型跳转:
Cmd/Ctrl + Click可快速查看源码定义和文档注释,是学习第三方库的高效方式 envFilePath支持动态指定.env文件路径,结合process.env.NODE_ENV实现多环境切换cross-env解决跨平台环境变量设置兼容性问题load属性:接受函数数组,每个函数返回键值对对象,用于加载共享配置- 配置覆盖规则:环境专属文件(
.env.development)> 共享文件(.env),同名键值后者覆盖前者
延伸学习资源
官方文档
后续课程预告
- 配置校验:使用
Joi对.env配置进行类型校验和必填校验 - 命名空间配置:按模块拆分配置文件的
forFeature()用法
配置管理最佳实践
.env → 公共配置(host、port 等通用值)
.env.development → 开发环境差异(dev 数据库、调试开关等)
.env.production → 生产环境差异(prod 数据库、性能配置等)
.env.example → 配置模板(提交到 Git,不含实际值)
.gitignore → 忽略 .env、.env.development、.env.production