首页 / 文章

使用Zod和TypeScript对Node.js环境变量进行验证。

停止在Node.js代码中到处读取process.env。通过Zod验证的env.ts文件可在启动时检测到缺失的密钥,并为应用提供类型化的TypeScript配置。

发布者:REACTAPP.TOP阅读约 3 分钟663 词

来源: https://medium.com/@vetriselvan_11/validating-environment-variables-in-node-js-using-zod-typescript-guide-16f4b6bacaf4

想象一下周五下午的部署场景:构建成功,服务器启动,大家都回家了。一小时后,用户反馈登录失败。原来是在生产环境中没人设置 JWT_SECRET。应用起初运行正常,直到第一个请求触碰到需要该缺失值的代码时才出问题。

在整个代码库中到处读取 process.env 会增加这类故障的发生概率。下文将展示如何用 Zod 和 TypeScript 替代这种做法,采用经过验证的类型化配置模块。

常规方法

大多数 Node.js 代码会在需要配置的地方直接获取它:一个文件中存放端口信息,另一个文件中存放数据库连接字符串。

// Some random controller file
const port = process.env.PORT || 3000;
mongoose.connect(process.env.MONGO_URI);

虽然很方便,但它存在三个严重问题。

  1. 没有类型安全。在 TypeScript 中,process.env.PORT 的类型为 string | undefined。如果需要数字类型,每个调用处都必须手动解析它。
  2. 错误不会显式提示。当缺少 MONGO_URI 时,进程在启动时不会崩溃,而是会在该行代码最终被执行时才出错,可能是在看似正常的部署数小时后。
  3. 没有统一的配置来源。新人无法获取应用所需的变量列表,他们唯一的办法就是在整个代码库中搜索 process.env。

一个统一的配置文件

解决办法是在业务逻辑中停止修改 process.env。创建一个名为 env.ts 的模块,在启动时读取一次环境变量,对其进行验证,然后导出一个强类型对象。其他所有模块都导入该对象。

以 TypeScript 为优先的架构验证库 Zod 负责执行验证工作。

安装它

npm install zod

编写架构定义

将该模块放在 src/config/env.ts 中,并详细描述环境应有的具体形态。

import dotenv from 'dotenv';
import { z } from 'zod';

// Load variables from .env file (if in development)
dotenv.config();

// 1. Define the schema
const envSchema = z.object({
  PORT: z.string().transform(Number).default('3000'),
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  MONGO_URI: z.string(),
  JWT_SECRET: z.string(),
  JWT_EXPIRES_IN: z.string().default('1d'),
});

// 2. Parse the environment against the schema
const _env = envSchema.safeParse(process.env);

// 3. Fail fast if it's invalid
if (!_env.success) {
  console.error('Invalid environment variables:', _env.error.format());
  process.exit(1); // Crash the app immediately!
}

// 4. Export the typed, validated object
export const env = _env.data;

该模块的功能

  • 它用于声明各种期望值。MONGO_URI是必填的字符串。PORT也以字符串形式提供,会通过.transform(Number)进行转换,默认值为3000。NODE_ENV必须为三个允许的值之一。
  • 它会快速检测错误。safeParse会在文件被导入时立即执行,因此请将其放在入口文件的最顶部,无论是app.ts还是server.ts。如果缺少任何必填变量或变量格式不正确,系统会报告失败,错误信息会明确指出问题所在,同时process.exit(1)会在处理请求之前终止进程。
  • 它会导出一个带类型的对象。解析成功后,数据会携带Zod推断出的类型,因此env.PORT已经是数字类型。

从那时起,任何文件都可以导入 env 并加以使用。

您将获得的好处

  • 自动补全功能。在编辑器中输入 env. 即可列出所有可用的变量。
  • 无需零散的备用方案。控制器中不再出现 || "default_value" 这类写法,因为默认值已存储在架构中。
  • 内置文档。架构文件详细列出了应用程序运行所需的一切,同时也能解答新手关于哪些内容应放在 .env 文件中的疑问。

现在,如果缺少密钥,系统会在启动时几秒钟内立即阻止部署,并给出明确提示,而不会等到一小时后才由愤怒的用户反映问题。