告別 process.env.UNDEFINED:在專案中實作型別安全的環境變數

5 分鐘

1. process.env 的「陷阱」

在 Node.js 應用程式中,我們透過 process.env 存取環境變數。這是一套簡單有效的機制,但它存在一個固有問題:型別不安全

process.env 物件的值不是 string,就是 undefined。這會造成幾個常見問題:

  • 意外的 undefined:如果你忘了在 .env 檔案或伺服器上設定某個變數,process.env.MY_VAR 在執行階段會是 undefined,可能在程式碼深層邏輯中導致 TypeError
  • 型別不符:你可能預期連接埠號是 number 型別,但 process.env.PORT 始終是字串,因此必須在使用它的地方手動呼叫 parseInt
  • 驗證邏輯分散:我們常在程式碼各處撰寫防禦性程式碼,例如 const port = process.env.PORT || 3000;,使環境變數的管理變得混亂。

2. 核心原則:啟動時失敗(Fail-Fast)

對環境變數這類關鍵設定而言,最佳實務是 「啟動時失敗」(Fail-Fast)

這代表應用程式應在啟動最初階段,就檢查所有必要環境變數是否均已提供且格式正確。若有任何問題,應用程式應立即擲回錯誤並停止執行,而不是在未來某個不確定的時間點因設定錯誤而崩潰。

這樣能讓我們在部署或開發時立即發現設定問題,而不是等到使用者存取時才發現。

3. 使用 Zod 實作型別安全驗證

Zod 是一個以 TypeScript 為優先的 schema 宣告與驗證函式庫,非常適合用來解決環境變數的型別安全問題。

我們的策略是:

  1. 為所有環境變數定義一個 Zod schema。
  2. 在應用程式啟動時,用這個 schema 解析 process.env
  3. 如果解析失敗,也就是驗證未通過,Zod 會擲回錯誤,應用程式啟動失敗。
  4. 如果解析成功,我們會得到一個完整型別化的物件,並在整個應用程式中使用它。

實作範例

首先安裝 Zod:pnpm add zod

接著,建立一個專門處理環境變數的檔案,例如 src/env.ts

// src/env.ts
import { z } from 'zod';

// 1. 定义 Schema
const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  DATABASE_URL: z.string().min(1, 'DATABASE_URL is required.'),
  PORT: z.coerce.number().int().positive().default(3000),
  // z.coerce 会尝试将字符串转换为数字
});

// 2. 解析和导出
// .parse 会在验证失败时抛出错误,实现 "Fail-Fast"
export const env = envSchema.parse(process.env);

現在,你可以在應用程式的任何其他地方從 src/env 匯入 env 物件。

// src/server.ts
import { env } from './env'; // 导入经过验证和类型化的 env 对象

// env.PORT 的类型是 `number`,而不是 `string | undefined`
const port = env.PORT;

// env.NODE_ENV 的类型是 'development' | 'production' | 'test'
if (env.NODE_ENV === 'development') {
  console.log('Running in development mode');
}

// 如果 DATABASE_URL 未设置,应用在启动时就已经崩溃了,
// 所以在这里我们可以放心地认为它是存在的,并且类型是 `string`。
connectToDatabase(env.DATABASE_URL);

4. 帶來的好處

這種模式能立即帶來好處:

  • 完整的型別安全:在程式碼中存取 env.PORT 時,TypeScript 知道它是一個 number
  • 集中的文件和驗證env.ts 檔案本身就成了環境變數的權威文件,所有驗證邏輯也都集中於此。
  • 可靠的執行階段:杜絕因環境變數拼寫錯誤、缺失或格式不正確而造成的執行階段 Bug。
  • Fail-Fast:任何設定問題都會在部署或開發的第一時間被發現。

結論

將環境變數的驗證提前到應用程式啟動時,並使用 Zod 這類工具保證型別安全,是一項投入產出比極高的工程實務。它能顯著提升應用程式的健全性與開發者信心,是現代 TypeScript 專案不可或缺的一環。