DEV Community

丁久
丁久

Posted on • Originally published at dingjiu1989-hue.github.io

Environment Variables: The Complete Guide for Developers

This article was originally published on AI Study Room. For the full version with working code examples and related articles, visit the original post.

Environment Variables: The Complete Guide for Developers

Environment variables connect your code to the outside world — database URLs, API keys, feature flags. Misconfiguring them is one of the most common causes of production incidents and security breaches. Here's the complete guide to managing them correctly.

The Hierarchy of Config

Layer Where Example Never Commit?
Default values Code (as fallback) PORT ?? 3000 Commit (with safe defaults)
Local dev overrides .env.local DATABASE_URL=localhost Yes (.gitignore)
CI/CD Platform secrets DATABASE_URL=staging-db Yes (platform-managed)
Production Platform secrets / vault DATABASE_URL=prod-db Yes (platform-managed)
Public config NEXT_PUBLIC_* vars NEXT_PUBLIC_API_URL OK (intentionally public)

Rules for Environment Variables

  1. Never commit secrets to Git. Use .gitignore for .env.local, .env.*.local. If a secret ever hits Git history, rotate it immediately.
  2. Prefix public variables. Next.js uses NEXT_PUBLIC_. Vite uses VITE_. This makes it clear what's exposed to the browser.
  3. Validate at startup, not at runtime. Use Zod to validate all env vars when the app starts. If a required var is missing, crash immediately — don't fail mysteriously 3 hours later.
  4. Use different values per environment. Development, staging, and production should have separate database URLs, API keys, and feature flags.

Validation Pattern (Prevent Runtime Surprises)

// env.ts — validate all env vars at startup
import { z } from "zod";

const envSchema = z.object({
DATABASE_URL: z.string().url(),
AUTH_SECRET: z.string().min(32),
STRIPE_SECRET_KEY: z.string().startsWith("sk_"),
NEXT_PUBLIC_APP_URL: z.string().url().default("http://localhost:3000"),
FEATURE_NEW_CHECKOUT: z.enum(["true", "false"]).default("false"),
});

export const env = envSchema.parse(process.env);
// If any var is missing or invalid, the app crashes immediately

Enter fullscreen mode Exit fullscreen mode




Managing Secrets Across a Team

Tool Best For How It Works
Doppler Teams, automatic sync Central dashboard → CLI syncs to local .env. Secrets never on disk.
Infisical Open source, self-hosted Self-hosted Doppler alternative. Inject secrets at build/run time.
1Password CLI Small teams with 1Password op run --env-file=.env -- npm run dev. Secret references, not values.
Platform-native Simplest, free Vercel/Render/Railway all have secret management built in.

Common Mistakes & Fixes

Mistake Fix
Hardcoding API keys in source Move to .env.local immediately. Check git history. Rotate if exposed.
NEXT_PUBLIC_* for secrets NEXT_PUBLIC_ vars are bundled into client JS. Anyone can see them. Never put secrets here.
Same API key for dev + prod Use separate keys. Stripe has test mode keys. Dev databases are separate.
.env.example not updated Add new vars to .env.example with dummy values. Treat it as documentation.
Secrets in Docker images Inject at runtime, not at build time. Use docker run -e or Docker secrets.

Bottom line: Validate env vars at startup with Zod. Never put secrets in NEXT_PUBLIC_* or Git. Use Doppler/Infisical for teams, platform-native for side projects. Document every variable in .env.example. See also: Web Security Basics and Error Handling Best Practices.


Read the full article on AI Study Room for complete code examples, comparison tables, and related resources.

Found this useful? Check out more developer guides and tool comparisons on AI Study Room.

Top comments (0)