← 中文资源

简体中文版本

Zod 嵌套环境配置解析

把扁平的环境变量解析成强类型嵌套配置:数字强制转换、可选段、特性开关白名单校验,启动即失败并输出完整问题清单。

阅读英文原文

示例说明

环境变量天然扁平且全是字符串,业务需要的却是带类型带层级的配置对象。Zod 的 transform 把解析与校验合二为一:逗号分隔的特性开关在转换时即校验白名单,未知开关当场报错而不是默默失效;safeParse 聚合全部问题一次性输出,运维改一次配置就能看到所有缺漏。

技术要点

  • z.coerce 处理字符串到数字的转型
  • transform 内嵌白名单校验逻辑
  • 启动时聚合输出全部配置问题

代码解读

loadConfig 把 process.env 整体交给 schema:必填的数据库地址缺失、端口越界、特性开关拼错都会成为 issue;报错消息把路径与原因拼接成清单。导出的 config 带完整类型,isProd 派生常量供全项目引用。

使用建议

敏感配置不要进默认值。特性开关数量增长后改用专用开关服务。staging 与 production 差异项显式列出,防止隐式继承导致的环境漂移。

代码示例

import { z } from 'zod';

const ConfigSchema = z.object({
  NODE_ENV: z.enum(['development', 'staging', 'production']),
  PORT: z.coerce.number().int().min(1).max(65535).default(3000),

  DATABASE_URL: z.string().url(),
  DATABASE_POOL_MAX: z.coerce.number().int().min(1).max(100).default(10),

  REDIS_URL: z.string().url().optional(),

  FEATURES: z.string().transform((raw, ctx) => {
    const flags = raw.split(',').map((f) => f.trim()).filter(Boolean);
    const known = ['audit', 'beta-ui', 'webhooks'] as const;
    for (const f of flags) {
      if (!known.includes(f as never)) {
        ctx.addIssue({ code: 'custom', message: 'unknown feature flag: ' + f });
        return z.NEVER;
      }
    }
    return flags;
  }).default(''),
});

type AppConfig = z.infer<typeof ConfigSchema>;

function loadConfig(source: Record<string, string | undefined>): AppConfig {
  const parsed = ConfigSchema.safeParse(source);
  if (!parsed.success) {
    const report = parsed.error.issues
      .map((i) => i.path.join('.') + ': ' + i.message)
      .join('; ');
    throw new Error('invalid environment configuration -> ' + report);
  }
  return parsed.data;
}

export const config = loadConfig(process.env as Record<string, string | undefined>);
export const isProd = config.NODE_ENV === 'production';