Support for asynchronous configuration loading with promise-based resolution and deferred initialization. Allows configuration values to be resolved asynchronously from external sources like databases, APIs, or files.
Create configuration values that resolve asynchronously using promises or functions.
/**
* Create async configuration value from promise or function
* @param promiseOrFunc - Promise that resolves to value, or function returning promise
* @returns Async configuration object with resolution capability
*/
function asyncConfig(promiseOrFunc: Promise<any> | ((config: object, original: any) => Promise<any>)): AsyncConfig;
interface AsyncConfig extends Promise<any> {
async: Symbol; // Marker identifying async configurations
prepare: Function; // Internal preparation function
release?: Function; // Release function for function-based async configs
}Usage Examples:
const { asyncConfig } = require('config/async');
// From promise
const dbConfig = asyncConfig(
fetch('/api/database-config')
.then(response => response.json())
);
// From function (also supports deferConfig functionality)
const apiKey = asyncConfig(function(config, original) {
return fetchSecretFromVault(config.vault.url);
});
// From async function
const cacheConfig = asyncConfig(async function(config, original) {
const env = config.environment;
const cacheSettings = await loadCacheSettingsForEnv(env);
return cacheSettings;
});Resolve all async configurations in a config object, returning a promise that resolves when all async values are loaded.
/**
* Resolve all async configurations in config object
* @param config - Configuration object containing async values
* @returns Promise that resolves to the original config object with all async values resolved
*/
function resolveAsyncConfigs(config: object): Promise<object>;Usage Examples:
const config = require('config');
const { resolveAsyncConfigs } = require('config/async');
// Resolve all async configurations
resolveAsyncConfigs(config)
.then(resolvedConfig => {
console.log('All async configs loaded');
console.log('Database host:', resolvedConfig.database.host);
console.log('API key:', resolvedConfig.api.key);
// Start application after config is fully loaded
startApplication(resolvedConfig);
})
.catch(error => {
console.error('Failed to load async configurations:', error);
process.exit(1);
});
// Using async/await
async function initializeApp() {
try {
const resolvedConfig = await resolveAsyncConfigs(config);
console.log('Configuration loaded successfully');
startApplication(resolvedConfig);
} catch (error) {
console.error('Configuration loading failed:', error);
process.exit(1);
}
}Async configurations can be defined directly in configuration files and will be resolved when resolveAsyncConfigs is called.
Usage Examples:
// In config/default.js
const { asyncConfig } = require('config/async');
module.exports = {
database: {
host: 'localhost',
port: 5432,
// Async configuration for database credentials
credentials: asyncConfig(async function(config) {
const vault = config.vault;
return await fetchCredentialsFromVault(vault.url, vault.path);
})
},
api: {
baseUrl: 'https://api.example.com',
// Async configuration from promise
token: asyncConfig(
fetch('/oauth/token', { method: 'POST', body: '...' })
.then(response => response.json())
.then(data => data.access_token)
)
},
cache: {
// Mixed sync and async configuration
enabled: true,
ttl: 3600,
redis: asyncConfig(async function(config) {
const env = process.env.NODE_ENV;
return await loadRedisConfigForEnvironment(env);
})
}
};
// In application code
const config = require('config');
const { resolveAsyncConfigs } = require('config/async');
async function startApp() {
// Resolve all async configurations
await resolveAsyncConfigs(config);
// Now all config values are available synchronously
const dbCredentials = config.get('database.credentials');
const apiToken = config.get('api.token');
const redisConfig = config.get('cache.redis');
console.log('All configurations loaded');
}Critical considerations when using async configurations.
Usage Examples:
const config = require('config');
const { resolveAsyncConfigs } = require('config/async');
// IMPORTANT: Do NOT use config.get() before resolving async configs
// This will freeze the config object and prevent async resolution
// ❌ WRONG - calling get() before resolveAsyncConfigs
const someValue = config.get('some.property'); // This freezes config
resolveAsyncConfigs(config); // This will fail
// ✅ CORRECT - resolve async configs first
resolveAsyncConfigs(config)
.then(() => {
const someValue = config.get('some.property'); // Now it's safe
});
// ✅ CORRECT - check for async configs before any access
async function safeConfigAccess() {
await resolveAsyncConfigs(config);
// Now safe to use any config access method
const dbConfig = config.get('database');
const hasCache = config.has('cache.enabled');
const apiUrl = config.api.baseUrl;
}
// Function-based async configs support release mechanism
const delayedConfig = asyncConfig(function(config, original) {
return new Promise(resolve => {
setTimeout(() => resolve('delayed-value'), 1000);
});
});
// Release mechanism is automatically handled during resolutionProper error handling for async configuration resolution failures.
// Async resolution can fail if any promise rejects
// Handle errors appropriately in application codeUsage Examples:
const { resolveAsyncConfigs } = require('config/async');
// Handle individual async config failures
const resilientAsyncConfig = asyncConfig(async function(config) {
try {
return await fetchExternalConfig();
} catch (error) {
console.warn('Failed to load external config, using defaults:', error.message);
return { timeout: 5000, retries: 3 }; // fallback values
}
});
// Handle overall resolution failures
async function loadConfigWithFallback() {
try {
await resolveAsyncConfigs(config);
console.log('All async configurations loaded successfully');
} catch (error) {
console.error('Some async configurations failed to load:', error);
// Decide whether to continue with partial config or exit
if (isConfigCritical(error)) {
console.error('Critical configuration missing, exiting');
process.exit(1);
} else {
console.log('Continuing with available configuration');
}
}
}
// Timeout handling for slow async configs
function createTimeoutAsyncConfig(promise, timeoutMs = 10000) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Async config timeout')), timeoutMs);
});
return asyncConfig(Promise.race([promise, timeoutPromise]));
}