Skip to content
__________________
V.1.0.0 // SECURE CONNECTION
Return_to_vault
[CONSTRUCT: 2026-01-22]

Zod Environment Validation

TypeScriptZodDevOps

Zod Environment Validation

Type-safe env var validation with separate schemas for server and client. Server-side vars get the full schema. Client-side only validates NEXT_PUBLIC_* vars (because that's all the browser can see). If validation fails in CI, it warns instead of crashing so your builds don't break over missing secrets in preview environments.

When to Use

  • Any Next.js app where you're tired of process.env.THING! assertions everywhere
  • Catching missing or malformed env vars at startup instead of at runtime when a user hits the broken path
  • CI pipelines where you need graceful degradation when not all secrets are available

The Code

import { z } from "zod";

const serverSchema = z.object({
  DATABASE_URL: z.string().url(),
  SESSION_SECRET: z.string().min(32),
});

const clientSchema = z.object({
  NEXT_PUBLIC_SITE_URL: z.string().url(),
});

const envSchema = clientSchema.merge(serverSchema);

const isServer = typeof window === "undefined";
const parsed = isServer
  ? envSchema.safeParse(process.env)
  : clientSchema.safeParse({
      NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL,
    });

let env = process.env as unknown as z.infer<typeof envSchema>;

if (parsed.success) {
  env = parsed.data as unknown as z.infer<typeof envSchema>;
} else {
  const isCI = !!process.env.SKIP_ENV_VALIDATION || !!process.env.CI;
  if (isCI) {
    console.warn("Skipping env validation in CI/Build.");
  } else {
    console.error("Invalid environment variables:",
      JSON.stringify(parsed.error.format(), null, 4));
  }
}

export { env };

Notes

The SKIP_ENV_VALIDATION escape hatch is intentional. Some CI environments (like Vercel preview deploys) don't have all secrets available. The fallback lets the build succeed while still catching real issues in production.

Share

"End of transmission."

[CLOSE_CONSTRUCT]