3. 实现环境变量管理
任务说明
在Node.js/TypeScript企业级Web应用中,不同环境(开发、测试、生产)需要不同的配置项,如数据库连接串、、日志级别等。直接将这些值硬编码在代码中会导致安全隐患和部署困难。本案例将创建一套环境变量管理方案:通过.env文件按环境区分配置,使用dotenv安全加载,并提供类型安全的访问接口,防止关键配置缺失时应用静默启动。
目录结构与依赖
project/
├── .env.development
├── .env.test
├── .env.production
├── .env.example
├── src/
│ └── config/
│ └── env.ts
├── .gitignore
└── package.json
需要安装依赖:npm install dotenv。TypeScript项目需同时安装@types/node。
实现代码
1. 环境配置文件(以开发环境为例,其他环境结构相同)
# .env.development
NODE_ENV=development
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_dev
DB_USER=app_user
DB_PASSWORD=dev_password_123
LOG_LEVEL=debug
=dev_key_abc123
2. 环境变量加载器(src/config/env.ts)
import dotenv from 'dotenv';
import path from 'path';
// 根据 NODE_ENV 加载对应文件,默认 development
const env = process.env.NODE_ENV || 'development';
const envFile = `.env.${env}`;
// 加载 .env 文件到 process.env,不覆盖已存在的系统环境变量
dotenv.config({ path: path.resolve(process.cwd(), envFile), override: false });
// 若 .env 文件不存在,dotenv 不会报错,需手动检查关键配置
const requiredVars = [
'NODE_ENV',
'PORT',
'DB_HOST',
'DB_PORT',
'DB_NAME',
'DB_USER',
'DB_PASSWORD',
'LOG_LEVEL',
''
] as const;
type EnvVar = typeof requiredVars[number];
// 检查必需变量是否全部存在
const missingVars = requiredVars.filter(
(varName) => !process.env[varName]
);
if (missingVars.length > 0) {
throw new Error(
`[配置错误] 缺少必需的环境变量: ${missingVars.join(', ')}。` +
`请检查 ${envFile} 文件是否存在且包含上述变量。`
);
}
// 类型安全的环境变量访问接口
export const envConfig = {
nodeEnv: process.env.NODE_ENV as string,
port: parseInt(process.env.PORT as string, 10),
db: {
host: process.env.DB_HOST as string,
port: parseInt(process.env.DB_PORT as string, 10),
name: process.env.DB_NAME as string,
user: process.env.DB_USER as string,
password: process.env.DB_PASSWORD as string
},
logLevel: process.env.LOG_LEVEL as string,
: process.env. as string
} as const;
// 导出环境名,便于其他模块判断
export const currentEnv = env;
3. 使用示例(src/index.ts)
import { envConfig, currentEnv } from './config/env';
console.log(`当前环境: ${currentEnv}`);
console.log(`服务端口: ${envConfig.port}`);
console.log(`数据库: ${envConfig.db.user}@${envConfig.db.host}:${envConfig.db.port}/${envConfig.db.name}`);
console.log(`日志级别: ${envConfig.logLevel}`);
// 实际应用中可这样使用
// app.listen(envConfig.port, () => {
// console.log(`Server running in ${envConfig.nodeEnv} mode on port ${envConfig.port}`);
// });
4. 辅助文件
# .env.example(提交到仓库的模板)
NODE_ENV=development
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=app_user
DB_PASSWORD=change_me
LOG_LEVEL=info
=change_me
# .gitignore
.env.development
.env.test
.env.production
// package.json 中的 scripts
{
"scripts": {
"dev": "cross-env NODE_ENV=development ts-node src/index.ts",
"test": "cross-env NODE_ENV=test jest",
"start": "cross-env NODE_ENV=production node dist/index.js"
}
}
安全加载机制说明
本方案的安全特性体现在三点:第一,.env.*文件被.gitignore排除,不会提交到版本库,避免密钥泄露;第二,override: false确保系统级环境变量优先于文件值,便于在CI/CD流水线中覆盖配置;第三,启动时强制校验必需变量,缺失即抛错终止进程,防止应用带着错误配置继续运行。生产环境部署时,只需在服务器上创建.env.production并填入真实值即可。
验证方法
验证步骤:
- 在项目根目录创建上述
.env.development文件,内容与示例一致; - 运行
npm run dev,观察控制台输出:应显示当前环境: development、服务端口: 3000、数据库连接信息为app_user@localhost:5432/myapp_dev、日志级别为debug; - 删除
.env.development文件后再次运行,应抛出错误[配置错误] 缺少必需的环境变量: ...,进程退出; - 执行
NODE_ENV=test node -e "require('dotenv').config({path:'.env.test'}); console.log(process.env.DB_NAME)",若.env.test中DB_NAME=myapp_test,则输出myapp_test,证明不同环境文件可正确切换。
判定标准:应用能按NODE_ENV值加载对应配置文件;缺失配置时启动失败并给出明确错误提示;.env.*文件不出现在版本库中;通过envConfig对象访问的配置值与文件内容一致。