Env-mage Docs
Guides

Best Practices

Recommended practices for managing environment variables with EnvWizard

Environment Variable Best Practices

This guide covers recommended practices for managing environment variables in your projects using EnvWizard.

General Principles

1. Security First

  • Never commit .env files containing sensitive information to version control
  • Use .gitignore to exclude .env files: .env* (but not .env.example)
  • Rotate secrets regularly and after team member departures
  • Use different variables for different environments (development, staging, production)

2. Consistency and Clarity

  • Use descriptive names that clearly indicate purpose
  • Follow consistent naming conventions
  • Document all variables in .env.example with comments
  • Group related variables together

3. Validation and Type Safety

  • Use EnvWizard's validation features to ensure required variables exist
  • Generate TypeScript types for environment variables
  • Add runtime validation for critical variables

Naming Conventions

CATEGORY_SUBCATEGORY_NAME=value

Examples

# ✅ Good examples
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY_STRIPE=sk_test_123456
FEATURE_FLAG_DARK_MODE=true

# ❌ Poor examples
DB=postgres://localhost:5432/mydb  # Too vague
stripeKey=sk_test_123456  # Inconsistent casing
DarkMode=true  # Not descriptive enough

Common Categories

  • APP_ - Application-specific settings
  • DATABASE_ - Database connections
  • API_KEY_ - External service API keys
  • FEATURE_FLAG_ - Feature toggles
  • SMTP_ - Email service configuration

File Organization

Standard Files

  • .env - Local development variables (never committed)
  • .env.example - Template with keys but no sensitive values (committed)
  • .env.test - Variables for test environment
  • .env.staging - Variables for staging environment
  • .env.production - Variables for production environment (never committed)

Example File Structure

# .env.example
# Database Configuration
DATABASE_URL=                   # URL to the database
DATABASE_USERNAME=              # Database username
DATABASE_PASSWORD=              # Database password

# API Keys
API_KEY_STRIPE=                 # Stripe payment processing
API_KEY_SENDGRID=               # Email delivery service

# Application Settings
APP_URL=http://localhost:3000   # Base application URL
APP_PORT=3000                   # Port the app runs on
DEBUG=false                     # Enable debug mode

EnvWizard Workflow

Setup Phase

  1. Create initial .env file for development
  2. Run env-mage init to generate .env.example
  3. Add comments and organize variables in .env.example
  4. Run env-mage typegen to generate TypeScript types
  5. Commit .env.example and generated type files

Development Phase

  1. Run env-mage validate before starting development
  2. Use env-mage scan to track variable usage
  3. Run env-mage lint to maintain consistent formatting
  4. Update .env.example when adding new variables

Deployment Phase

  1. Run env-mage diff to compare environments
  2. Use env-mage sync to align environment files
  3. Run env-mage validate --strict to ensure all required variables exist

Environment Variables in Different Frameworks

Next.js

  • Prefix public variables with NEXT_PUBLIC_
  • Use .env.local for local overrides
  • Set up per-environment variables with .env.development, .env.production

React with Vite

  • Prefix variables with VITE_
  • Access variables with import.meta.env.VITE_VARIABLE

Node.js/Express

  • Load variables early in the application bootstrap
  • Validate critical variables before server startup

Continuous Integration

  • Set up CI checks to validate .env.example against required variables
  • Run env-mage validate in CI pipeline with a CI-specific env file
  • Add env-mage scan to detect unused variables

Pre-commit Hooks

Use Husky and lint-staged to set up pre-commit hooks:

// package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": ["env-mage scan --quiet"],
    ".env.example": ["env-mage lint --quiet"]
  }
}

Type Safety

Runtime Validation

Combine EnvWizard's types with runtime validation:

// env-validation.ts
import { z } from "zod";
import "env.types.ts"; // Generated by EnvWizard

const envSchema = z.object({
  DATABASE_URL: z.string().url(),
  API_KEY: z.string().min(10),
  PORT: z.string().regex(/^\d+$/).transform(Number),
  DEBUG: z.enum(["true", "false"]).transform((val) => val === "true"),
});

export function validateEnv() {
  const parsed = envSchema.safeParse(process.env);

  if (!parsed.success) {
    console.error("❌ Invalid environment variables:");
    console.error(parsed.error.format());
    process.exit(1);
  }

  return parsed.data;
}

// Use the validated environment
export const env = validateEnv();

Secrets Management

For production environments, consider:

  1. Using a secrets manager like:

    • AWS Secrets Manager
    • Google Cloud Secret Manager
    • HashiCorp Vault
    • Azure Key Vault
  2. Integrating with EnvWizard:

    # Pull secrets into .env.production (never committed)
    ./scripts/pull-secrets.sh
    
    # Validate the environment
    env-mage validate --env .env.production --strict

Conclusion

Following these best practices will help you maintain secure, consistent, and reliable environment variables across your projects. EnvWizard's suite of tools makes implementing these practices easier and more automated, reducing the chance of errors and security issues related to environment configuration.