ข้ามไปเนื้อหาหลัก

✓ เสร็จแล้ว

type-safe-env

TypeScript utility สำหรับ parse และ validate environment variables ด้วย Zod — crash ทันทีถ้า env ขาดหาย แทนที่จะ fail แบบ silent

· อ่านประมาณ 1 นาที

สารบัญ

ภาพรวม

ปัญหาที่พบบ่อย: application รันไปได้สักพักแล้วค่อย crash ด้วย Cannot read properties of undefined เพราะ process.env.SOME_KEY เป็น undefined โดยไม่รู้ตัวตั้งแต่ start

โปรเจกต์นี้เป็น utility ที่รับ Zod schema แล้ว parse process.env ตั้งแต่ต้น — ถ้าขาด key หรือ type ผิด จะ throw ทันทีพร้อม error message บอกว่าขาด variable อะไร

วิธีใช้งาน

// env.ts
import { parseEnv } from './type-safe-env';
import { z } from 'zod';

export const env = parseEnv(
  z.object({
    NODE_ENV: z.enum(['development', 'production', 'test']),
    PORT: z.coerce.number().int().min(1).max(65535).default(3000),
    DATABASE_URL: z.string().url(),
    JWT_SECRET: z.string().min(32),
    LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
  })
);

// type-safe ทันที:
env.PORT          // number
env.NODE_ENV      // 'development' | 'production' | 'test'
env.DATABASE_URL  // string

ถ้า DATABASE_URL ขาดหาย:

Error: Environment variable validation failed:
  DATABASE_URL: Required
  JWT_SECRET: String must contain at least 32 character(s)

สิ่งที่ทำ

  • รับ Zod schema แล้ว parse process.env ทั้งหมด
  • collect errors ทุกตัวก่อน throw ครั้งเดียว (ไม่ throw ทีละตัว)
  • รองรับ .default() ใน schema
  • รองรับ z.coerce.number() สำหรับ PORT, TIMEOUT, etc.
  • export type ของผลลัพธ์อัตโนมัติจาก schema

ปัญหาที่เจอ

Zod z.coerce ทำงานก่อน parse ดังนั้น z.coerce.number().min(1) จะ coerce 'abc' เป็น NaN ก่อน แล้วค่อย fail ที่ .min(1) — error message จึงออกมาเป็น “Number must be greater than or equal to 1” ไม่ใช่ “Invalid number” จัดการโดยเพิ่ม custom message ใน .coerce.number({ invalid_type_error: 'Must be a number' })

เทคโนโลยี

  • TypeScript 5
  • Zod 3
  • Node.js 20+