dotenv-expand
Expands variable references, command substitution, and encrypted values in dotenv-parsed .env files.
Repository Health
Technical Analysis
dotenv-expand extends dotenv with variable interpolation, letting one .env value reference another via ${VAR} or $VAR syntax, along with default and alternate value fallbacks. It also supports command substitution via $(command) and can decrypt dotenvx-encrypted values when a private key is present in the environment.
Built as a thin orchestration layer over the @dotenvx/primitives package, it’s typically chained directly after dotenv.config() to expand the parsed result before it’s written into process.env, making layered and cross-referencing .env configuration possible without introducing a full templating engine.
What You Get
- Variable expansion that resolves ${VAR} and unbraced $VAR references against process.env and already-expanded keys
- Default and alternate value syntax — ${MISSING:-default} and ${BASIC:+alternate} — for conditional fallbacks
- Command substitution — $(command) executes and inlines shell output into a value
- Encrypted value decryption — automatically decrypts dotenvx
encrypted:values when DOTENV_PRIVATE_KEY is set - Preload support via
node -r dotenv-expand/config, expanding .env before your app starts with no separate dotenv install required
Common Use Cases
- Composing DATABASE_URL from separately defined HOST/PORT/USER variables
- Preloading expanded env vars into a script via -r dotenv-expand/config in CI or npm scripts
- Deriving a value at load time with command substitution, e.g. capturing
node --versioninto an env var - Shipping dotenvx-encrypted .env files that only decrypt into expanded values when a DOTENV_PRIVATE_KEY is supplied at runtime
Under The Hood
Architecture
lib/main.js exports a single expand(options) function that takes options.parsed (the key-value map from dotenv.config()) and iterates each key while building up a runningParsed object for progressive expansion, so later variables can reference earlier ones already resolved within the same call. For each value it first checks a short-circuit case: if the target processEnv already holds a different value for that key, the pre-existing value wins untouched. Otherwise it builds a shadow copy of processEnv, strips entries that just mirror unexpanded literals (so already-expanded values from the running pass are preferred over raw pass-through data dotenv may have set), checks for a DOTENV_PRIVATE_KEY against dotenvx’s encrypted: convention and decrypts via @dotenvx/primitives, then calls that package’s expand and evaluate helpers for substitution and command evaluation. A thin config.js wrapper combines dotenv’s own CLI-arg/env-var option parsing with this expand() to power the -r dotenv-expand/config preload entrypoint. All substitution/decryption logic lives in the separate @dotenvx/primitives package; this repo is the orchestration layer over dotenv’s parsed output rather than a self-contained implementation.
Tech Stack
Plain CommonJS JavaScript with no framework — 'use strict', require/module.exports throughout. The only runtime dependency is the first-party @dotenvx/primitives package (pinned to 2.1.1 in devDependencies alongside dotenv ^17.4.2 for testing), which supplies decrypt, encrypted, evaluate, and expand. The build uses esbuild to bundle lib/main.js and config.js into dist/ as CJS targeting Node 16, with hand-written type declarations in lib/main.d.ts validated separately against tests/types via tsc. Linting is done with Biome rather than ESLint/Prettier. Deployment target is npm registry publish, with prepack running the esbuild build and prepublishOnly doing a dry-run pack check.
Code Quality
Tests live in tests/main.js using a small custom tap-style harness (tests/test.js), covering expansion, self-referential expansion (guarding against infinite recursion), default/alternate value syntax, encrypted-value decryption using @dotenvx/primitives’ encrypt/keypair helpers directly in the test file, and pre-existing process.env short-circuiting, with deliberate process.env = {} reset in beforeEach for isolation. npm test runs the harness directly with node, no external test-runner dependency. The pretest script chains Biome lint and a dts-check (tsc against tests/types/tsconfig.json), so type declarations are validated even though the library itself isn’t authored in TypeScript. Error handling is intentionally non-throwing — unexpandable or self-referential values resolve to an empty string rather than raising, a reasonable choice for a startup-time env loader that shouldn’t crash a caller’s boot sequence. Naming is short and consistent (_resolveEscapeSequences, runningParsed, literals).
API Design
The public surface is a single expand(options) function accepting { parsed, processEnv? }, mirroring dotenv’s own config() return shape so the two chain naturally: dotenvExpand.expand(dotenv.config()). This keeps boilerplate minimal — one extra line after the existing dotenv call — and the optional processEnv override lets callers redirect expansion into an object other than process.env without any additional API surface. Naming and option shapes deliberately parallel dotenv’s own conventions, reducing the learning curve for anyone already using dotenv. Documentation in the README is thorough, with worked examples for every feature (preload, command substitution, encrypted values, custom processEnv, interpolation syntax) presented as collapsible detail blocks.
Used by 13 apps in this directory
APITable
Low Code Platforms · Databases
API-first collaborative spreadsheet-database platform that auto-generates REST APIs and lets teams build internal tools, CRMs, and dashboards without code.
Appsmith
Developer Tools · Automation · No Code Platforms
Open-source low-code platform to build admin panels, dashboards, and internal tools connected to any database or API.
ChartBrew
Analytics · Databases
Open-source reporting platform to build live dashboards from SQL, NoSQL, APIs, and SaaS tools with an AI assistant that creates charts from natural language.
Ghostfolio
Invoicing Finance
Track your stocks, ETFs, and crypto with a privacy-first, self-hostable wealth management platform built for data-driven investors.
HyperDX
Developer Tools · Analytics · Monitoring
Open source observability platform that unifies logs, traces, metrics, and session replays on ClickHouse — now the core of ClickStack.
Langfuse
AI Development · Monitoring
Open source AI engineering platform for LLM observability, prompt management, evaluation, and debugging — self-host in minutes or use Langfuse Cloud.
Laudspeaker
Marketing · Automation
Open-source customer engagement platform for building visual, event-triggered messaging journeys across email, SMS, push, in-app, and webhooks.
LobeHub
AI Assistants · Productivity · Automation
Your Chief Agent Operator — build, schedule, and collaborate with an entire AI team in one self-hostable workspace.
medusa
Ecommerce
The most flexible open-source commerce platform — build B2C, B2B, and marketplace applications with modular, composable commerce primitives.