Loads environment variables from .env file into process.env with support for encrypted .env.vault files
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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 setInstall with Tessl CLI
npx tessl i tessl/npm-dotenv