Configuration Management
Last Updated: December 7, 2024
Status: ✅ Production Ready
Related: Usage Guide | Testing Guide
Overview
All application configuration is centralized in src/config.ts. This provides a single source of truth for environment variables with type safety, validation, and ESLint enforcement.
Core Principles
- Single Source of Truth - Only
src/config.tsaccessesprocess.env - Type Safety - All config values are properly typed
- Validation - Built-in validation for critical settings
- ESLint Enforcement - Direct
process.envaccess blocked - Config-Centric - Validation checks values, not env var names
Architecture
Configuration Module
The module exports config grouped by domain:
// Runtime
export const nodeEnv: string;
export const isDevelopment: boolean;
export const isProduction: boolean;
// Server
export const serverConfig: { port, vercelUrl, baseUrl };
// WorkOS/Auth
export const workosConfig: { apiKey, clientId, cookiePassword, ... };
// JWT & Encryption
export const authConfig: { jwtSecret, encryptionKey, ... };
// Database
export const databaseConfig: { host, port, user, password, database };
// Migrations
export const migrationConfig: { skipOnStartup, requireSuccess };
// Features
export const featureConfig: { enableSetupRouteRedirect, dsiBaseUrl };
// Security
export const securityConfig: { vpnWhitelist };
// CI/CD
export const ciConfig: { isCI, forceColor, useColors };
Validation
export interface ConfigValidationResult {
valid: boolean;
errors: string[];
warnings: string[];
}
// Validate critical config (required for app)
export function validateCriticalConfig(): ConfigValidationResult;
// Validate important config (may limit features)
export function validateImportantConfig(): ConfigValidationResult;
// Validate all
export function validateConfig(): ConfigValidationResult;
Quick Start
Basic Usage
// ❌ WRONG - ESLint error
const apiKey = process.env.WORKOS_API_KEY;
// ✅ CORRECT
import { workosConfig } from '@/config';
const apiKey = workosConfig.apiKey;
Environment Checks
// ❌ WRONG
if (process.env.NODE_ENV === 'development') { }
// ✅ CORRECT
import { isDevelopment } from '@/config';
if (isDevelopment) { }
Validation
import { validateConfig } from '@/config';
const validation = validateConfig();
if (!validation.valid) {
console.error('Configuration errors:', validation.errors);
}
ESLint Enforcement
Only these files can access process.env:
src/config.ts- the config module itselfnext.config.ts- Next.js framework configvitest.config.ts- test framework configinstrumentation.ts- Next.js instrumentationsrc/lib/tool/tool.ts- passes env to child processes
All other files must import from @/config.
Adding New Configuration
Step 1: Add to Config
// In src/config.ts
export const myServiceConfig = {
apiUrl: getEnv('MY_SERVICE_API_URL'),
apiKey: getEnv('MY_SERVICE_API_KEY'),
timeout: getEnvInt('MY_SERVICE_TIMEOUT', 5000),
enabled: getEnvBool('MY_SERVICE_ENABLED', false),
} as const;
Step 2: Add Validation (if critical)
export function validateMyServiceConfig(): ConfigValidationResult {
const errors: string[] = [];
if (myServiceConfig.enabled && !myServiceConfig.apiKey) {
errors.push('Missing API key when service enabled');
}
return { valid: errors.length === 0, errors, warnings: [] };
}
Step 3: Use in Code
import { myServiceConfig } from '@/config';
if (myServiceConfig.enabled) {
await fetch(myServiceConfig.apiUrl!, {
headers: { 'Authorization': `Bearer ${myServiceConfig.apiKey}` }
});
}
Helper Functions
// Get required (throws if missing)
function getEnvRequired(key: string): string;
// Get optional with default
function getEnv(key: string, defaultValue?: string): string | undefined;
// Parse integer
function getEnvInt(key: string, defaultValue: number): number;
// Parse boolean
function getEnvBool(key: string, defaultValue?: boolean): boolean;
Benefits
✅ Single Source - All config in one place
✅ Type Safe - TypeScript types throughout
✅ Testable - Mock one module vs many env vars
✅ Validated - Catch errors at startup
✅ Secure - Controlled access to sensitive values
✅ Future-Proof - Config source can change
✅ Enforced - ESLint prevents violations
Troubleshooting
ESLint Error: "Direct access to process.env not allowed"
Solution: Import from @/config:
// Before
const value = process.env.MY_VAR;
// After
import { myConfig } from '@/config';
const value = myConfig.myVar;
Need to Whitelist a File
Only whitelist if legitimately needed (framework config, spawning processes):
- Add to
eslint.config.mjsfiles array - Document why it needs the exception
Config Values Undefined
- Check
.env.examplefor required variables - Copy to
.env.developmentand set values - Restart dev server
Further Reading
- Usage Guide - Detailed examples and patterns
- Testing Guide - Mocking and test strategies
- Implementation - How we built this
References
- Source:
src/config.ts - ESLint:
eslint.config.mjs - Validation:
src/lib/db/migrations/startup.ts - Environment Variables:
.env.example