示例说明
环境间的差异配置如果靠口口相传必然出错。分层加载给出确定规则:先读共享默认文件,再读当前环境专属文件覆盖差异项,容器平台注入的进程环境天然最高优先级。启动时校验必填变量,缺失直接退出,配置问题在部署阶段暴露而不是运行深处。
技术要点
- 加载顺序即覆盖优先级
- 必填变量缺失在启动时快速失败
- Object.freeze 防止运行期篡改配置
代码解读
dotenv 的 config 把文件键值写入 process.env,后加载的文件按顺序覆盖前面同名的值;quiet 抑制多余的加载日志。settings 把扁平的环境变量整理成带类型的嵌套结构,featureFlags 支持逗号分隔列表解析,最后冻结防止业务代码偷偷改配置。
使用建议
默认文件提交进仓库,环境专属文件加入 gitignore 并提供模板。密钥类配置走平台密钥管理而不是文件。配置对象全项目单例导出,禁止散落的二次读取。
代码示例
import { config } from 'dotenv';
const ENV = process.env.NODE_ENV ?? 'development';
// 优先级从低到高:共享默认 → 环境专属 → 容器注入的进程环境
config({ path: '.env.defaults', quiet: true });
config({ path: `.env.${ENV}`, quiet: true });
const layered = { ...process.env };
const required = ['DATABASE_URL', 'REDIS_URL'];
const missing = required.filter((key) => !layered[key]);
if (missing.length > 0) {
console.error('missing required env vars:', missing.join(', '));
process.exit(1);
}
export const settings = Object.freeze({
env: ENV,
databaseUrl: layered.DATABASE_URL,
redisUrl: layered.REDIS_URL,
featureFlags: (layered.FEATURE_FLAGS ?? '').split(',').filter(Boolean),
http: {
port: Number(layered.PORT ?? 3000),
bodyLimitMb: Number(layered.BODY_LIMIT_MB ?? 1),
},
});
console.log('config resolved for', settings.env, 'port', settings.http.port);