Validate ENV Variables in TypeScript with Zod
Validating environment variables in TypeScript is essential for preventing runtime failures caused by missing or misconfigured values. Zod offers a clean, type‑safe way to verify all required variables before your app starts.

Why ENV Validation Matters
Configuration errors are among the most common causes of production crashes. By validating ENV variables at startup, you ensure:
- Required values exist
- Types are correct
- URLs, ports, keys, and modes match expectations
- Your app fails early, not unpredictably at runtime
Zod makes this process extremely reliable and fully typed across the entire project.
Step 1: Install Dependencies
npm install dotenv zodLoad variables before running validation:
import dotenv from "dotenv";
dotenv.config();Step 2: Create a Strongly Typed Zod Schema
import { z } from "zod";
export const envSchema = z.object({
NODE_ENV: z.enum(["development", "production", "test"]),
PORT: z.string().transform(Number).refine(p => p > 1000 && p < 5000, {
message: "PORT must be a number between 1000 and 5000",
}),
DATABASE_URL: z.string().url(),
AUTH_SECRET: z.string().min(1, "AUTH_SECRET must not be empty"),
});Improvements in this version
- Better numeric comparison
- Clearer messages
-
min(1)used to catch empty strings - Schema exported for global typing
Step 3: Validate and Export Typed ENV Values
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error("❌ Invalid ENV configuration:");
console.error(parsed.error.flatten().fieldErrors);
process.exit(1);
}
export const env = parsed.data;Benefits
-
safeParseavoids throwing errors - Graceful shutdown on misconfiguration
- Fully typed
envobject for safe use everywhere
Step 4: Add Global Types for process.env
Create: src/types/env.d.ts
import { envSchema } from "../env";
import { z } from "zod";
declare global {
namespace NodeJS {
interface ProcessEnv extends z.infer<typeof envSchema> {}
}
}Now TypeScript warns you about typos or missing variables.
Full Example
// src/env.ts
import dotenv from "dotenv";
import { z } from "zod";
dotenv.config();
export const envSchema = z.object({
NODE_ENV: z.enum(["development", "production", "test"]),
PORT: z.string().transform(Number).default("3000"),
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(1),
});
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error("ENV validation failed:", parsed.error.format());
process.exit(1);
}
export const env = parsed.data;Best Practices for ENV Validation
- Never trust runtime values — always validate
- Use
.min(1)for secrets to avoid empty strings - Use
.url()for database/storage endpoints - Add default values only when truly optional
- Fail fast in production environments
Alternatives
- Joi — powerful but less TypeScript‑friendly
- Valibot — lightweight validator
- Runtypes — similar runtime validation
- dotenv‑typesafe — simpler but less flexible
Conclusion
Validating environment variables with Zod brings:
- Safety
- Clear errors
- Predictable startup behavior
- Full type inference across your app
This small setup prevents major runtime bugs and ensures your configuration stays clean and reliable.