Environment & Config Manager
Manages environment variables, validates .env files, generates typed config objects, and ensures consistency across environments.
Environment & Config Manager
Manage application configuration across development, staging, and production environments with type-safe access, secret handling, and validation at startup.
When to Use This Template
Choose Environment & Config Manager when:
- Your app reads from .env files and you need type validation and defaults
- You have multiple environments (dev, staging, prod) with different configurations
- You want to fail fast at startup if required environment variables are missing
- Secrets management needs separation from regular configuration
Consider alternatives when:
- You need enterprise secrets management (use AWS Secrets Manager or HashiCorp Vault)
- Your configuration is purely infrastructure-level (use Terraform variables)
- You need runtime feature flags (use LaunchDarkly or Unleash)
Quick Start
# .claude/skills/environment-config-manager.yml name: environment-config-manager description: Manage typed environment config with validation prompt: | Create or update environment configuration management. Requirements: 1. Type-safe config access (no raw process.env) 2. Validation at startup — fail fast on missing required vars 3. Defaults for optional values 4. Separate secret handling (never logged or serialized) 5. Environment-specific overrides 6. Generate .env.example from config schema Use Zod or similar for schema validation.
Example invocation:
claude "Set up typed environment config for the API with database, Redis, and Stripe settings"
Generated config module:
// src/config/index.ts import { z } from 'zod'; const configSchema = z.object({ NODE_ENV: z.enum(['development', 'staging', 'production']).default('development'), PORT: z.coerce.number().default(3000), // Database DATABASE_URL: z.string().url(), DATABASE_POOL_SIZE: z.coerce.number().min(1).max(100).default(10), // Redis REDIS_URL: z.string().url().optional(), // Stripe (secrets — never logged) STRIPE_SECRET_KEY: z.string().startsWith('sk_'), STRIPE_WEBHOOK_SECRET: z.string().startsWith('whsec_'), }); export const config = configSchema.parse(process.env); export type Config = z.infer<typeof configSchema>;
Core Concepts
Configuration Layers
| Layer | Priority | Source | Example |
|---|---|---|---|
| Defaults | Lowest | Schema definition | PORT: 3000 |
| .env file | Low | .env, .env.local | Development overrides |
| Environment | Medium | process.env, container env | Staging/prod values |
| CLI flags | Highest | --port 8080 | Debug overrides |
Type-Safe Configuration with Zod
import { z } from 'zod'; import dotenv from 'dotenv'; // Load .env files based on NODE_ENV const envFile = process.env.NODE_ENV === 'test' ? '.env.test' : '.env'; dotenv.config({ path: envFile }); const envSchema = z.object({ // Server NODE_ENV: z.enum(['development', 'staging', 'production', 'test']), PORT: z.coerce.number().default(3000), HOST: z.string().default('0.0.0.0'), LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'), // Database DATABASE_URL: z.string().url(), DB_SSL: z.coerce.boolean().default(false), DB_POOL_MIN: z.coerce.number().default(2), DB_POOL_MAX: z.coerce.number().default(10), // Auth JWT_SECRET: z.string().min(32), JWT_EXPIRES_IN: z.string().default('7d'), SESSION_SECRET: z.string().min(32), // External Services SMTP_HOST: z.string().optional(), SMTP_PORT: z.coerce.number().default(587), SMTP_USER: z.string().optional(), SMTP_PASS: z.string().optional(), // Feature Flags ENABLE_SIGNUP: z.coerce.boolean().default(true), MAINTENANCE_MODE: z.coerce.boolean().default(false), }); // Validate at startup const parsed = envSchema.safeParse(process.env); if (!parsed.success) { console.error('Invalid environment configuration:'); for (const issue of parsed.error.issues) { console.error(` ${issue.path.join('.')}: ${issue.message}`); } process.exit(1); } export const env = parsed.data; // Secret masking for logging const SECRET_KEYS = ['JWT_SECRET', 'SESSION_SECRET', 'SMTP_PASS', 'DATABASE_URL']; export function getSafeConfig(): Record<string, string> { return Object.fromEntries( Object.entries(env).map(([key, value]) => [ key, SECRET_KEYS.includes(key) ? '***REDACTED***' : String(value) ]) ); }
Environment File Generation
// Generate .env.example from schema function generateEnvExample(schema: z.ZodObject<any>): string { const lines: string[] = ['# Environment Configuration', '']; const shape = schema.shape; for (const [key, zodType] of Object.entries(shape)) { const isOptional = zodType.isOptional(); const defaultVal = zodType._def?.defaultValue?.(); const description = zodType.description || ''; let example = ''; if (defaultVal !== undefined) example = String(defaultVal); else if (key.includes('URL')) example = 'https://...'; else if (key.includes('SECRET') || key.includes('KEY')) example = 'your-secret-here'; else example = ''; lines.push(`# ${description || key}${isOptional ? ' (optional)' : ' (required)'}`); lines.push(`${key}=${example}`); lines.push(''); } return lines.join('\n'); }
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
schema | string | "zod" | Validation library: zod, joi, yup, custom |
envFiles | string[] | [".env", ".env.local"] | Dotenv files to load (in order) |
failOnMissing | boolean | true | Exit process if required vars are missing |
maskSecrets | boolean | true | Redact secret values in logs |
generateExample | boolean | true | Auto-generate .env.example file |
secretPrefix | string[] | ["SECRET_", "KEY_", "TOKEN_"] | Patterns to treat as secrets |
Best Practices
-
Validate all config at startup, not at usage — Parse and validate the entire environment configuration when the application boots. If
STRIPE_SECRET_KEYis missing, fail immediately with a clear error message rather than crashing 3 hours later when a user tries to check out. -
Never use
process.envdirectly in application code — Access configuration through the validated config object. This guarantees type safety, ensures defaults are applied, and creates a single source of truth. Search your codebase for rawprocess.envusage periodically. -
Keep .env.example in version control, never .env — The
.env.examplefile documents every configuration variable with descriptions and safe example values. New developers copy it to.envand fill in real values. Add.env*(except.env.example) to.gitignore. -
Separate secrets from regular config — Secrets (API keys, database passwords, JWT secrets) should be loaded from a secure source in production (Vault, AWS Secrets Manager, encrypted env vars) rather than plain .env files. Your config module should support multiple sources transparently.
-
Use environment-specific defaults wisely — Development defaults should enable local development without any
.envfile (e.g., SQLite database, localhost URLs). Production should have no defaults for critical values — force explicit configuration to prevent accidental misconfiguration.
Common Issues
Boolean environment variables parse incorrectly — process.env.ENABLE_FEATURE is the string "false", which is truthy in JavaScript. Use Zod's z.coerce.boolean() or explicitly check value === 'true' rather than relying on JavaScript truthiness. This is the most common source of config bugs.
Dotenv files override production environment variables — If dotenv.config() runs in production, it can override environment variables set by the deployment platform. Only load .env files in development, or use dotenv.config({ override: false }) to ensure system environment variables take precedence over file values.
Config changes require application restart — Environment variables are read once at startup and cached. If you change a value in production, the running process still uses the old value. For values that need runtime updates without restarts, use a config service (like Consul or a database-backed config) with a polling mechanism.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.