or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

environment-loading.mdenvironment-population.mdfile-parsing.mdindex.mdvault-decryption.md
tile.json

environment-population.mddocs/

Environment Population

Utility for merging parsed environment variables into target objects like process.env with flexible override options and conflict resolution.

Capabilities

Populate Function

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' }

Population Options

Override Behavior

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)

Debug Logging

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 overwritten

Advanced Usage

Selective Population

Populate 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' }

Population Chain

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' }

Type-Safe Population

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']

Error Handling

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 {};
  }
}

Return Types

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