| name | managing-configuration |
| description | Loads and validates configuration once at boot through a typed schema (Zod / class-validator), preventing scattered process.env access and runtime surprises. Use when adding env vars, debugging config-related crashes, or onboarding a new environment. |
| license | MIT |
Managing Configuration
When to use
- Adding new environment variables
- Debugging config-related crashes
- Onboarding a new environment
- Removing scattered
process.env access
Core rules
- Read
process.env exactly once at boot
- Validate with Zod (preferred) or class-validator
- Export typed
AppConfig object for the rest of the code
- No
process.env outside the config module
- Secrets never logged or returned in responses
Reference shape (TypeScript)
Zod Schema (Preferred)
import { z } from 'zod';
const configSchema = z.object({
NODE_ENV: z.enum(['development', 'staging', 'production', 'test']),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
});
export type AppConfig = z.infer<typeof configSchema>;
let _config: AppConfig | null = null;
export function loadConfig(): AppConfig {
if (_config) return _config;
const parsed = configSchema.safeParse(process.env);
if (!parsed.success) {
console.error('Config validation failed:', parsed.error.format());
process.exit(1);
}
_config = parsed.data;
return _config;
}
Examples — Do
export const config = loadConfig();
import { config } from '@shared/config';
const port = config.PORT;
Examples — Don't
if (process.env.NODE_ENV === 'production') { ... }
const dbUrl = process.env.DATABASE_URL;
Checklist
See reference/config-patterns.md for full patterns.