Config Manager
Type-safe environment variable handling with validation for AWS Lambda functions and Node.js applications.
Overview
The Config Manager module provides validated environment variable getters and converters with comprehensive type safety:
- Environment Variable Getters: Type-safe retrieval of environment variables with validation
- Environment Variable Converters: Validated conversion of values to environment variable strings
- Zod Schema Validation: Built-in validation for strings, numbers, and booleans
- Error Handling: Clear error messages for invalid environment variables
Environment Variable Getters
Type-safe functions to retrieve and validate environment variables at runtime.
String Variables
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
// Get a validated string environment variable
const apiUrl = envVar.getString('API_URL');
// Returns: string (validated to be 2-100 characters)
const databaseName = envVar.getString('DATABASE_NAME');
// Throws error if DATABASE_NAME is undefined, empty, or invalid length
Boolean Variables
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
// Get a validated boolean environment variable
const enableDebug = envVar.getBoolean('ENABLE_DEBUG');
// Returns: boolean (from "true" or "false" string)
const enableCache = envVar.getBoolean('ENABLE_CACHE');
// Throws error if ENABLE_CACHE is not "true" or "false" (case-insensitive)
Number Variables
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
// Get a validated number environment variable
const port = envVar.getNumber('PORT');
// Returns: number (parsed from string)
const maxRetries = envVar.getNumber('MAX_RETRIES');
// Throws error if MAX_RETRIES is not a valid number or contains only whitespace
Environment Variable Converters
Validated conversion functions to prepare values for setting as environment variables.
String Conversion
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
// Validate and prepare string for environment variable
const validatedString = envVar.fromString({
variableName: 'API_ENDPOINT',
value: 'https://api.example.com'
});
// Returns: 'https://api.example.com' (validated)
// Set environment variable
process.env.API_ENDPOINT = validatedString;
Boolean Conversion
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
// Convert boolean to environment variable string
const debugString = envVar.fromBoolean({
variableName: 'DEBUG_MODE',
value: true
});
// Returns: 'true'
const cacheString = envVar.fromBoolean({
variableName: 'ENABLE_CACHE',
value: false
});
// Returns: 'false'
// Set environment variables
process.env.DEBUG_MODE = debugString;
process.env.ENABLE_CACHE = cacheString;
Number Conversion
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
// Convert number to environment variable string
const portString = envVar.fromNumber({
variableName: 'SERVER_PORT',
value: 3000
});
// Returns: '3000'
const timeoutString = envVar.fromNumber({
variableName: 'TIMEOUT_MS',
value: 5000
});
// Returns: '5000'
// Set environment variables
process.env.SERVER_PORT = portString;
process.env.TIMEOUT_MS = timeoutString;
Lambda Integration Examples
Configuration Validation at Startup
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
import { withHttpHandler } from '@leighton-digital/lambda-toolkit/lambda';
// Validate configuration at module load time
const config = {
apiUrl: envVar.getString('API_URL'),
enableDebug: envVar.getBoolean('ENABLE_DEBUG'),
maxRetries: envVar.getNumber('MAX_RETRIES'),
timeout: envVar.getNumber('TIMEOUT_MS'),
};
export const handler = withHttpHandler(async ({ event, metrics }) => {
// Configuration is already validated and typed
const response = await fetch(config.apiUrl, {
timeout: config.timeout,
});
if (config.enableDebug) {
metrics.addMetric('DebugRequest', 1);
}
return {
statusCode: 200,
body: { message: 'Success' },
};
});
Dynamic Configuration
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
export const handler = withHttpHandler(async ({ event }) => {
// Get stage-specific configuration
const stage = envVar.getString('STAGE');
const tableName = envVar.getString(`TABLE_NAME_${stage.toUpperCase()}`);
// Use stage-specific settings
const enableAdvancedFeatures = envVar.getBoolean(`ENABLE_ADVANCED_${stage.toUpperCase()}`);
return {
statusCode: 200,
body: {
stage,
tableName,
advancedFeaturesEnabled: enableAdvancedFeatures,
},
};
});
Environment Variable Preparation for CDK
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
import { Function } from 'aws-cdk-lib/aws-lambda';
export class MyLambdaStack extends Stack {
constructor(scope: Construct, id: string, props: StackProps) {
super(scope, id, props);
// Prepare environment variables with validation
const environment = {
API_URL: envVar.fromString({
variableName: 'API_URL',
value: 'https://api.production.com'
}),
ENABLE_CACHE: envVar.fromBoolean({
variableName: 'ENABLE_CACHE',
value: true
}),
MAX_CONNECTIONS: envVar.fromNumber({
variableName: 'MAX_CONNECTIONS',
value: 100
}),
};
new Function(this, 'MyFunction', {
// ... other props
environment,
});
}
}
Validation Rules
String Validation
- Minimum length: 2 characters
- Maximum length: 100 characters
- Throws error: If undefined, empty, or outside length bounds
Boolean Validation
- Valid values: "true" or "false" (case-insensitive)
- Conversion: Transforms to actual boolean type
- Throws error: If not exactly "true" or "false"
Number Validation
- Format: Must be a valid numeric string
- Whitespace: Cannot be only whitespace
- Maximum length: 10 characters
- Conversion: Transforms to actual number type
- Throws error: If not a valid number or NaN
Error Handling
All validation errors provide clear, actionable error messages:
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
try {
const invalidString = envVar.getString('MISSING_VAR');
} catch (error) {
console.error(error.message);
// "Invalid environment variable: MISSING_VAR: [ZodError details]"
}
try {
const invalidBoolean = envVar.getBoolean('INVALID_BOOL');
} catch (error) {
console.error(error.message);
// "Invalid environment variable: INVALID_BOOL: [ZodError details]"
}
try {
const invalidNumber = envVar.getNumber('INVALID_NUM');
} catch (error) {
console.error(error.message);
// "Invalid environment variable: INVALID_NUM: [ZodError details]"
}
Advanced Examples
Configuration Factory
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
interface AppConfig {
database: {
host: string;
port: number;
ssl: boolean;
};
api: {
baseUrl: string;
timeout: number;
retries: number;
};
features: {
enableCache: boolean;
enableMetrics: boolean;
};
}
function loadConfig(): AppConfig {
return {
database: {
host: envVar.getString('DB_HOST'),
port: envVar.getNumber('DB_PORT'),
ssl: envVar.getBoolean('DB_SSL'),
},
api: {
baseUrl: envVar.getString('API_BASE_URL'),
timeout: envVar.getNumber('API_TIMEOUT_MS'),
retries: envVar.getNumber('API_MAX_RETRIES'),
},
features: {
enableCache: envVar.getBoolean('ENABLE_CACHE'),
enableMetrics: envVar.getBoolean('ENABLE_METRICS'),
},
};
}
// Load and validate all configuration at startup
const config = loadConfig();
Testing with Environment Variables
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
describe('Environment Configuration', () => {
const originalEnv = process.env;
beforeEach(() => {
process.env = { ...originalEnv };
});
afterAll(() => {
process.env = originalEnv;
});
it('should load valid configuration', () => {
// Set test environment variables
process.env.API_URL = envVar.fromString({
variableName: 'API_URL',
value: 'https://test-api.example.com'
});
process.env.ENABLE_DEBUG = envVar.fromBoolean({
variableName: 'ENABLE_DEBUG',
value: true
});
process.env.PORT = envVar.fromNumber({
variableName: 'PORT',
value: 3000
});
// Test getters
expect(envVar.getString('API_URL'))
.toBe('https://test-api.example.com');
expect(envVar.getBoolean('ENABLE_DEBUG'))
.toBe(true);
expect(envVar.getNumber('PORT'))
.toBe(3000);
});
});
Environment-Specific Configuration
import { envVar } from '@leighton-digital/lambda-toolkit/config-manager';
function getEnvironmentConfig(stage: string) {
const configs = {
development: {
apiUrl: 'https://dev-api.example.com',
enableDebug: true,
maxConnections: 10,
},
staging: {
apiUrl: 'https://staging-api.example.com',
enableDebug: false,
maxConnections: 50,
},
production: {
apiUrl: 'https://api.example.com',
enableDebug: false,
maxConnections: 100,
},
};
const stageConfig = configs[stage] || configs.development;
return {
API_URL: envVar.fromString({
variableName: 'API_URL',
value: stageConfig.apiUrl
}),
ENABLE_DEBUG: envVar.fromBoolean({
variableName: 'ENABLE_DEBUG',
value: stageConfig.enableDebug
}),
MAX_CONNECTIONS: envVar.fromNumber({
variableName: 'MAX_CONNECTIONS',
value: stageConfig.maxConnections
}),
};
}
Best Practices
- Early Validation: Load and validate environment variables at module initialization to fail fast
- Type Safety: Use the typed getters to ensure runtime type safety
- Error Handling: Wrap configuration loading in try-catch blocks for graceful error handling
- Testing: Use converters to prepare test environment variables with proper validation
- Documentation: Document required environment variables and their validation rules
- Defaults: Consider using fallback patterns for optional configuration values
- Security: Never log environment variable values, especially sensitive data like API keys
Dependencies
Required
zod- Schema validation library for runtime type checking
Peer Dependencies
None - this module works standalone with any Node.js environment.
Environment Variable Naming
Follow these conventions for consistency:
- Use
UPPER_SNAKE_CASEfor environment variable names - Use descriptive names that indicate purpose (e.g.,
DATABASE_URL,ENABLE_FEATURE_X) - Include units in names when relevant (e.g.,
TIMEOUT_MS,MAX_SIZE_MB) - Use prefixes for grouped variables (e.g.,
DB_HOST,DB_PORT,DB_NAME)