环境配置
加载、验证、分层环境变量,让应用在缺配置时快速失败。
为什么
未定义的环境变量是'本地能跑、prod 挂'的头号原因——变量在你的 .env 里但不在平台面板里,应用在凌晨 3 点用 500 发现它。验证层(zod、envalid 或 pydantic)把它变成启动崩溃带清晰消息,所以部署在 CI 而非 prod 失败。分层(defaults < .env < .env.local < platform)让你把密钥排除 git 同时与团队共享非密钥配置。没有它,每个开发者维护个人 .env 漂移,入职要花一天'我需要哪些变量?'。
何时使用
应用读第一个环境变量时。用 zod 约 20-40 分钟。仅对零配置纯静态站可跳过。
如何做
1. 用 zod 定义 schema,列出每个变量的类型 + 默认值 + 描述
- const schema = z.object({ DATABASE_URL: z.string().url(), PORT: z.coerce.number().default(3000) })
2. 启动时解析 process.env,导出类型化单例
- const env = schema.parse(process.env)——缺第一个变量即抛错
- 在一处调用(src/env.ts);所有人从那里 import
3. 创建 .env.example 含每个 key + 注释,提交它
- 永不提交 .env;第 1 次提交就加进 .gitignore
4. 分层优先级:.env.defaults < .env < .env.local < 平台变量
- 用 dotenv-flow 或 Next.js 内置分层
- .env.local 存开发密钥(gitignore);平台存 prod 密钥
5. 快速失败:在模块顶层调用 env 解析,不在请求 handler 里
- 缺变量应在启动时崩溃,而非第一个请求时示例
// src/env.ts
import { z } from "zod";
const schema = z.object({
DATABASE_URL: z.string().url(),
NEXTAUTH_SECRET: z.string().min(32),
NEXTAUTH_URL: z.string().url(),
PORT: z.coerce.number().default(3000),
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
});
export const env = schema.parse(process.env);
// → 若 DATABASE_URL 缺失,应用启动即崩:
// "Invalid input: DATABASE_URL: Required"常见问题
- ⚠在 20 个文件里直接读 process.env 而非一个模块——改变量名要全仓搜索。
- ⚠把所有变量当字符串,PORT='3000' + 1 运行时得 '30001' 而非 3001。
- ⚠把密钥放 .env 并提交'给团队用'——git 历史现在永远含 Stripe key。
- ⚠启动时不验证,应用正常启动,第一次 DB 查询才崩,报晦涩连接错误。
最佳实践
- ✓数字和布尔用 z.coerce,'PORT=3000' 直接变真数字无需手动 parseInt。
- ✓加 CI 步骤用 schema 验证 .env.example——抓住你加到代码但忘了文档化的变量。
- ✓错误 handler 里永不 log 整个 env 对象——你会把密钥泄漏给日志聚合器。
- ✓prod 用密钥管理器(Doppler、Infisical)而非把值粘进平台面板——轮换变成配置变更而非部署。