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并填入真实值即可。

验证方法

验证步骤:

  1. 在项目根目录创建上述.env.development文件,内容与示例一致;
  2. 运行npm run dev,观察控制台输出:应显示当前环境: development、服务端口: 3000、数据库连接信息为app_user@localhost:5432/myapp_dev、日志级别为debug;
  3. 删除.env.development文件后再次运行,应抛出错误[配置错误] 缺少必需的环境变量: ...,进程退出;
  4. 执行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对象访问的配置值与文件内容一致。

环境配置文件 .env.development NODE_ENV=development PORT=3000 DB_HOST=localhost =dev_key_abc123 .env.test NODE_ENV=test PORT=3100 DB_HOST=test-db =test_key_xyz789 .env.production NODE_ENV=production PORT=8080 DB_HOST=prod-db =prod_key_secure dotenv.config() 读取 NODE_ENV 拼接文件名 .env.${env} override: false 写入 process.env 必需变量校验 requiredVars 数组 filter 检查缺失项 缺失 → throw Error 全部存在 → 导出 envConfig 类型安全对象 业务代码使用 import { envConfig, currentEnv } from './config/env'; app.listen(envConfig.port, ...) 按 NODE_ENV 选择 注入 安全校验