Node.js 中使用env文件

Node.js 中 dotenv、pm2 与 process.env 的正确使用姿势

------为什么 pm2 能读到 env,而 node xxx.js 读不到?

这是一个我在真实项目中踩到的坑:
同一份代码,用 pm2 启动一切正常,但直接 node xxx.js 就报 MySQL Access denied for user ''

最终定位到:dotenv 并没有在当前 Node 进程中执行


一、问题现象

报错信息

text 复制代码
Access denied for user ''@'localhost' (using password: NO)

从报错可以看出两个关键点:

  • MySQL 用户名是空字符串:user ''
  • 没有使用密码:using password: NO

.env 明明已经写了:

env 复制代码
DB_USER=root
DB_PASSWORD=xxxx

而且:

  • 用 pm2 启动服务一切正常
  • node testApi/deleteUser.js 就直接报错

二、核心结论(一句话版)

.env 只是一个文件,不是环境变量
dotenv 只会在"当前 Node 进程"里生效一次

所以:

  • pm2 启动的进程 👉 一开始就有 env
  • 手动 node xxx.js 👉 没有 dotenv,就没有 env

三、dotenv 到底做了什么?

js 复制代码
require('dotenv').config()

这行代码的作用只有一个:

.env 文件的内容注入到 process.env

它不会:

  • 自动全局生效
  • 自动影响其他 Node 进程
  • 自动影响你之后手动 node xxx.js 启动的脚本

四、为什么 pm2 能读到 env,而 node 不行?

1️⃣ pm2 的常见行为

pm2 通常通过以下方式之一提供环境变量:

ecosystem.config.js
js 复制代码
env: {
  DB_USER: 'root',
  DB_PASSWORD: 'xxx'
}
shell 环境变量
bash 复制代码
export DB_USER=root
pm2 start index.js
pm2 启动目录中的 .env

👉 pm2 启动 Node 进程时,process.env 已经被填充


2️⃣ 手动 node 启动的情况

bash 复制代码
node testApi/deleteUser.js

如果这个文件里:

  • ❌ 没有 require('dotenv').config()
  • ❌ shell 里也没有 export env

那么:

js 复制代码
process.env.DB_USER === undefined
process.env.DB_PASSWORD === undefined

最终 MySQL 连接配置就会变成:

js 复制代码
user: ''
password: undefined

五、dotenv 写在哪才是"正确的"?

✅ 正确原则(非常重要)

dotenv 只应该写在「进程入口文件」里

什么是"入口文件"?

任何一个你可能直接运行的文件:

bash 复制代码
node index.js
node testApi/deleteUser.js
node scripts/migrate.js

这些 都是入口


六、正确示例

✅ 示例 1:最简单、安全(推荐)

js 复制代码
// testApi/deleteUser.js
require('dotenv').config()

const { deleteById } = require('../repos/userRepo')
deleteById(1)
js 复制代码
// repos/userRepo.js
// 不要写 dotenv
console.log(process.env.DB_USER) // 可以正常读取

✅ 示例 2:统一 bootstrap(工程级推荐)

js 复制代码
// bootstrap.js
require('dotenv').config()
js 复制代码
// index.js
require('./bootstrap')
require('./app')
js 复制代码
// testApi/deleteUser.js
require('../bootstrap')
require('../repos/userRepo')

👉 所有入口统一初始化环境


七、为什么不要在 export 文件里写 dotenv?

❌ 错误示例:

js 复制代码
// repos/userRepo.js
require('dotenv').config() // ❌ 不推荐

原因:

  • 模块职责不清晰
  • 被 require 时机不可控
  • 单测 / 脚本 / CLI 混乱
  • 容易造成"有时能读到、有时读不到"

八、一个非常实用的防坑技巧(强烈建议)

在创建数据库连接前,直接硬失败

js 复制代码
const must = (key) => {
  const val = process.env[key]
  if (!val) throw new Error(`${key} is missing`)
  return val
}

mysql.createPool({
  host: must('DB_HOST'),
  user: must('DB_USER'),
  password: must('DB_PASSWORD'),
  database: must('DB_NAME'),
})

👉 以后不会再看到:

text 复制代码
user ''
using password: NO

九、最终总结(工程级结论)

✅ dotenv 只影响当前 Node 进程

✅ pm2 ≠ node

✅ dotenv 只属于「入口文件」

❌ export 的模块里不要写 dotenv

记住一句话就够了:

"dotenv 是进程初始化的一部分,不是业务逻辑的一部分。"

相关推荐
DongHao2 小时前
跨域问题及解决方案
前端·javascript·面试
持续升级打怪中2 小时前
Vue项目中Axios全面封装实战指南
前端·javascript·vue.js
a17798877122 小时前
print.js打印
前端·javascript·html
Sport2 小时前
用全会,问全废:CSS高频面试题
前端·javascript·面试
Maxkim2 小时前
「✍️JS原子笔记 」零基础吃透 Proxy 数据响应式
前端·javascript·面试
AllinLin2 小时前
javaScript学习计划(Day26-30)
开发语言·javascript·学习
DO_Community3 小时前
加速 JavaScript 开发:DigitalOcean 应用托管现已原生支持 Bun
开发语言·前端·javascript
m0_672656543 小时前
React 使用 JSX 来替代常规的 JavaScript。
前端·javascript·react.js
Dreamcatcher_AC3 小时前
Node.js留言板开发全流程解析
前端·javascript·mysql·node.js·express