Lazy-loading configuration values that are computed on first access using provided functions. Allows configuration values to be calculated dynamically based on other configuration values or runtime conditions.
Create configuration values that are computed lazily when first accessed.
/**
* Create deferred configuration value
* @param func - Function called on first access to compute the value
* @returns Deferred configuration object
*/
function deferConfig(func: (config: object, original: any) => any): DeferredConfig;
interface DeferredConfig {
prepare: (config: object, prop: object, property: string) => DeferredConfig;
resolve: () => any;
}Usage Examples:
const { deferConfig } = require('config/defer');
// Deferred computation based on other config values
const computedTimeout = deferConfig(function(config) {
const baseTimeout = config.server.baseTimeout;
const multiplier = config.server.timeoutMultiplier || 1;
return baseTimeout * multiplier;
});
// Deferred environment-based configuration
const logLevel = deferConfig(function(config) {
return config.environment === 'production' ? 'error' : 'debug';
});
// Deferred configuration with complex logic
const databaseUrl = deferConfig(function(config) {
const db = config.database;
const auth = db.username && db.password
? `${db.username}:${db.password}@`
: '';
return `postgresql://${auth}${db.host}:${db.port}/${db.name}`;
});Deferred configurations can be defined directly in configuration files and will be resolved on first access.
Usage Examples:
// In config/default.js
const { deferConfig } = require('config/defer');
module.exports = {
server: {
host: 'localhost',
port: 3000,
baseTimeout: 5000,
timeoutMultiplier: 2,
// Deferred configuration computed from other values
timeout: deferConfig(function(config) {
return config.server.baseTimeout * config.server.timeoutMultiplier;
}),
// Deferred URL construction
url: deferConfig(function(config) {
const server = config.server;
return `http://${server.host}:${server.port}`;
})
},
database: {
host: 'localhost',
port: 5432,
name: 'myapp',
username: 'user',
password: 'pass',
// Deferred connection string
connectionString: deferConfig(function(config) {
const db = config.database;
return `postgresql://${db.username}:${db.password}@${db.host}:${db.port}/${db.name}`;
})
},
logging: {
level: deferConfig(function(config) {
return process.env.NODE_ENV === 'production' ? 'info' : 'debug';
}),
file: deferConfig(function(config) {
const level = config.logging.level; // Can reference other deferred values
return `./logs/${level}.log`;
})
}
};
// In application code
const config = require('config');
// Deferred values are computed on first access
console.log('Server URL:', config.get('server.url')); // Computed here
console.log('Database connection:', config.get('database.connectionString'));
// Direct property access also triggers computation
const timeout = config.server.timeout; // Computed hereDeferred values are computed once on first access and then cached for subsequent access.
// Deferred values are computed on first access and cached
// The computation function receives:
// - config: The full configuration object
// - original: The original value before deferred processing (if any)Usage Examples:
const { deferConfig } = require('config/defer');
// Accessing other deferred values from within deferred function
const complexConfig = deferConfig(function(config) {
// This can access other deferred values
const serverUrl = config.server.url; // Will be computed if not already
const dbHost = config.database.host;
return {
serviceUrl: `${serverUrl}/api`,
healthCheck: `${serverUrl}/health`,
dbConnection: `${dbHost}:${config.database.port}`
};
});
// Deferred configuration with caching behavior
let computeCount = 0;
const expensiveComputation = deferConfig(function(config) {
computeCount++;
console.log(`Computing expensive value (call #${computeCount})`);
// Simulate expensive computation
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.random();
}
return result;
});
// First access computes the value
const value1 = config.get('expensiveComputation'); // Logs: "Computing expensive value (call #1)"
// Subsequent access uses cached value
const value2 = config.get('expensiveComputation'); // No log, uses cached value
console.log(value1 === value2); // trueProper error handling for deferred configuration computation failures.
Usage Examples:
const { deferConfig } = require('config/defer');
// Deferred configuration with error handling
const robustConfig = deferConfig(function(config) {
try {
const result = performComplexComputation(config);
return result;
} catch (error) {
console.error('Failed to compute deferred config:', error.message);
return getDefaultValue(); // Return fallback value
}
});
// Deferred configuration with validation
const validatedConfig = deferConfig(function(config) {
const computed = computeValue(config);
if (!isValidValue(computed)) {
throw new Error(`Invalid computed configuration value: ${computed}`);
}
return computed;
});
// Handle errors when accessing deferred values
const config = require('config');
try {
const value = config.get('validatedConfig');
console.log('Computed value:', value);
} catch (error) {
console.error('Deferred configuration computation failed:', error.message);
// Handle error or use fallback
}
// Defensive deferred configuration
const safeConfig = deferConfig(function(config) {
// Check prerequisites
if (!config.hasRequiredProperty) {
console.warn('Required property missing, using default');
return 'default-value';
}
// Validate dependencies
const dependency = config.dependency;
if (typeof dependency !== 'string') {
throw new Error('Dependency must be a string');
}
return `computed-${dependency}`;
});Complex patterns and best practices for deferred configurations.
Usage Examples:
const { deferConfig } = require('config/defer');
// Conditional deferred configuration
const conditionalConfig = deferConfig(function(config) {
if (config.features.advancedMode) {
return loadAdvancedConfiguration(config);
} else {
return loadBasicConfiguration(config);
}
});
// Deferred configuration with external dependencies
const externalConfig = deferConfig(function(config) {
// Access external systems during configuration resolution
const fs = require('fs');
const path = require('path');
const configPath = path.join(config.paths.configDir, 'external.json');
if (fs.existsSync(configPath)) {
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
} else {
return { external: false };
}
});
// Deferred configuration with circular reference handling
const circularSafeConfig = deferConfig(function(config) {
// Be careful not to create circular references
// This is safe because it doesn't reference the property being computed
const baseValue = config.base.value;
const multiplier = config.multiplier || 1;
return baseValue * multiplier;
});
// Deferred configuration for different environments
const environmentConfig = deferConfig(function(config) {
const env = process.env.NODE_ENV || 'development';
const envConfigs = {
development: { debug: true, timeout: 1000 },
production: { debug: false, timeout: 5000 },
test: { debug: false, timeout: 100 }
};
return envConfigs[env] || envConfigs.development;
});