Utility for merging parsed environment variables into target objects like process.env with flexible override options and conflict resolution.
Populates a target object (like process.env) with parsed environment variables from a source object.
/**
* Populates target object with parsed environment variables
* @param processEnv - Target object to populate (typically process.env)
* @param parsed - Source object with key-value pairs to merge
* @param options - Population options for override and debug behavior
* @returns Object containing keys and values that were actually set
*/
function populate(
processEnv: DotenvPopulateInput,
parsed: DotenvPopulateInput,
options?: DotenvConfigOptions
): DotenvPopulateOutput;Usage Examples:
const dotenv = require('dotenv');
// Basic population
const parsed = { DATABASE_URL: 'postgres://localhost/mydb', API_KEY: 'secret' };
const populated = dotenv.populate(process.env, parsed);
console.log(populated);
// { DATABASE_URL: 'postgres://localhost/mydb', API_KEY: 'secret' }
// With override option
process.env.EXISTING_VAR = 'original';
const newVars = { EXISTING_VAR: 'updated', NEW_VAR: 'new' };
const result = dotenv.populate(process.env, newVars, { override: true });
console.log(result);
// { EXISTING_VAR: 'updated', NEW_VAR: 'new' }
// With debug logging
dotenv.populate(process.env, parsed, { debug: true });
// Logs: [dotenv@17.2.2][DEBUG] "DATABASE_URL" is already defined and was NOT overwritten
// Populate custom object
const config = {};
dotenv.populate(config, { NODE_ENV: 'development', PORT: '3000' });
console.log(config);
// { NODE_ENV: 'development', PORT: '3000' }Control whether existing variables are overwritten.
interface OverrideOptions {
/** Override existing environment variables (default: false) */
override?: boolean;
}Examples:
const dotenv = require('dotenv');
// Existing variable protection (default)
process.env.NODE_ENV = 'production';
dotenv.populate(process.env, { NODE_ENV: 'development' });
console.log(process.env.NODE_ENV); // 'production' (unchanged)
// Override existing variables
process.env.NODE_ENV = 'production';
dotenv.populate(process.env, { NODE_ENV: 'development' }, { override: true });
console.log(process.env.NODE_ENV); // 'development' (overridden)
// Mixed scenario
process.env.EXISTING = 'keep';
const result = dotenv.populate(process.env, {
EXISTING: 'change',
NEW_VAR: 'add'
}, { override: false });
console.log(result);
// { NEW_VAR: 'add' } - only new variables returned
console.log(process.env.EXISTING); // 'keep' (unchanged)
console.log(process.env.NEW_VAR); // 'add' (set)Enable detailed logging of population behavior.
interface DebugOptions {
/** Enable debug logging (default: false) */
debug?: boolean;
}Examples:
// Enable debug mode
process.env.EXISTING_VAR = 'original';
dotenv.populate(process.env, {
EXISTING_VAR: 'new',
FRESH_VAR: 'value'
}, { debug: true });
// Console output:
// [dotenv@17.2.2][DEBUG] "EXISTING_VAR" is already defined and was NOT overwritten
// [dotenv@17.2.2][DEBUG] "FRESH_VAR" set
// With override enabled
dotenv.populate(process.env, {
EXISTING_VAR: 'updated'
}, { debug: true, override: true });
// Console output:
// [dotenv@17.2.2][DEBUG] "EXISTING_VAR" is already defined and WAS overwrittenPopulate only specific variables based on conditions:
const dotenv = require('dotenv');
function populateSelective(target, source, filter) {
const filtered = {};
Object.keys(source).forEach(key => {
if (filter(key, source[key])) {
filtered[key] = source[key];
}
});
return dotenv.populate(target, filtered);
}
// Only populate development variables
const devFilter = (key, value) => key.startsWith('DEV_') || key === 'NODE_ENV';
const config = {};
populateSelective(config, {
DEV_DATABASE_URL: 'localhost:5432',
PROD_DATABASE_URL: 'prod:5432',
NODE_ENV: 'development',
SECRET_KEY: 'secret'
}, devFilter);
console.log(config);
// { DEV_DATABASE_URL: 'localhost:5432', NODE_ENV: 'development' }Chain multiple population operations:
const dotenv = require('dotenv');
function populateChain(target, ...sources) {
const results = [];
sources.forEach((sourceConfig, index) => {
const { source, options = {} } = sourceConfig;
const result = dotenv.populate(target, source, options);
results.push({ step: index + 1, populated: result });
});
return results;
}
// Usage
const config = {};
const results = populateChain(config,
{
source: { BASE_URL: 'http://localhost', PORT: '3000' },
options: { debug: true }
},
{
source: { BASE_URL: 'https://production.com', ENV: 'prod' },
options: { override: true, debug: true }
}
);
console.log(config);
// { BASE_URL: 'https://production.com', PORT: '3000', ENV: 'prod' }Create type-safe wrappers for population:
const dotenv = require('dotenv');
class TypedEnvironment {
constructor(target = {}) {
this.env = target;
}
populate(source, options = {}) {
return dotenv.populate(this.env, source, options);
}
getString(key, defaultValue = '') {
return this.env[key] || defaultValue;
}
getNumber(key, defaultValue = 0) {
const value = this.env[key];
return value ? parseInt(value, 10) : defaultValue;
}
getBoolean(key, defaultValue = false) {
const value = this.env[key];
if (!value) return defaultValue;
return ['true', '1', 'yes', 'on'].includes(value.toLowerCase());
}
getArray(key, separator = ',', defaultValue = []) {
const value = this.env[key];
return value ? value.split(separator).map(s => s.trim()) : defaultValue;
}
}
// Usage
const env = new TypedEnvironment();
env.populate({
PORT: '3000',
DEBUG: 'true',
FEATURES: 'auth,billing,analytics'
});
console.log(env.getNumber('PORT')); // 3000
console.log(env.getBoolean('DEBUG')); // true
console.log(env.getArray('FEATURES')); // ['auth', 'billing', 'analytics']Handle invalid inputs and edge cases:
const dotenv = require('dotenv');
try {
// Invalid parsed object
dotenv.populate(process.env, null);
} catch (error) {
console.log(error.code); // 'OBJECT_REQUIRED'
console.log(error.message); // 'OBJECT_REQUIRED: Please check the processEnv argument being passed to populate'
}
try {
// Invalid target object
dotenv.populate(null, { KEY: 'value' });
} catch (error) {
console.log('Population failed:', error.message);
}
// Safe populate function
function safePopulate(target, source, options = {}) {
try {
if (!target || typeof target !== 'object') {
throw new Error('Target must be an object');
}
if (!source || typeof source !== 'object') {
throw new Error('Source must be an object');
}
return dotenv.populate(target, source, options);
} catch (error) {
console.error('Population error:', error.message);
return {};
}
}interface DotenvPopulateInput {
[name: string]: string;
}
interface DotenvPopulateOutput {
[name: string]: string;
}
interface DotenvPopulateOptions {
/** Enable debug logging (default: false) */
debug?: boolean;
/** Override existing environment variables (default: false) */
override?: boolean;
}The return value contains only the key-value pairs that were actually set in the target object:
process.env.EXISTING = 'keep';
const result = dotenv.populate(process.env, {
EXISTING: 'change', // Won't be set (no override)
NEW_VAR: 'add' // Will be set
});
console.log(result);
// { NEW_VAR: 'add' } - only variables that were actually set