Loader
loadEnv and loadEnvFile are the two file-loading entry points. resetEnv undoes them.
Signatures
function loadEnv(envPath?: string, options?: EnvLoaderOptions): voidfunction loadEnvFile(envPath: string, override: boolean): voidfunction resetEnv(): void
type EnvLoaderOptions = { override?: boolean; // default true — also write into process.env dir?: string; // default cwd() — search root loadSharedEnv?: boolean; // default true — load .env.shared first};File resolution (when envPath is omitted)
- If
loadSharedEnvistrueand${dir}/.env.sharedexists, load it first. - Try
${dir}/.env.${process.env.NODE_ENV}(e.g..env.development). - If that file does not exist, fall back to
${dir}/.env.
process.env.NODE_ENV = "development";loadEnv();// → .env.shared (if present)// → .env.developmentprocess.env.NODE_ENV = "test"; // no .env.test on diskloadEnv();// → .env.shared (if present)// → .envloadEnv("/etc/secrets.env"); // explicit path skips the resolver, // but .env.shared is still loaded first // unless loadSharedEnv: falseOverride semantics
| Setting | Internal store | process.env |
|---|---|---|
override: true (default) | Typed value (number / boolean / null / string) | Written, then coerced to string by Node |
override: false | Typed value | Untouched |
loadEnv(undefined, { override: false });
process.env.APP_PORT; // undefined (not written)env("APP_PORT"); // 3000 (in store anyway)Layering
APP_NAME="My App"APP_URL="https://example.com"
# .env.productionDB_HOST=prod-db.example.comDEBUG=falseprocess.env.NODE_ENV = "production";loadEnv();
env("APP_NAME"); // "My App" — from .env.sharedenv("DB_HOST"); // "prod-db.example.com" — from .env.productionenv("DEBUG"); // falseIf a key appears in both files, the environment-specific file wins (it loads second, and with override: true writes through).
loadEnvFile — the low-level form
loadEnvFile("/abs/path/to/.env", /* override */ true);Loads exactly one file. Throws if the path does not exist:
Error: .env file not found at /abs/path/to/.envUse this when you need to load a file outside the standard resolution chain (e.g. a secrets.env somewhere on disk, deferred loading, multiple env files at different paths).
resetEnv
resetEnv();Does:
- Clears every key from the internal store.
- Deletes any
process.envkeys thatloadEnvFilewrote since module load (tracked internally in aSet). - Re-assigns every key in the import-time
process.envsnapshot back toprocess.env.
The net effect is a true “back to t0” for anything the loader added. Keys that callers set directly on process.env (without going through loadEnv / loadEnvFile) are not tracked and survive the reset — the caller owns their own additions.
Common loading patterns
// 1. Boot at startup, defaults.import { loadEnv } from "@mongez/dotenv";loadEnv();// 2. Read-only — populate store but don't touch process.env.loadEnv(undefined, { override: false });// 3. Custom directory (monorepo with env files in a sibling).loadEnv(undefined, { dir: path.resolve(__dirname, "../config") });// 4. Skip the shared layer (rare).loadEnv(undefined, { loadSharedEnv: false });// 5. Multiple files with explicit ordering.import { loadEnvFile } from "@mongez/dotenv";loadEnvFile("/etc/myapp/base.env", true);loadEnvFile("/etc/myapp/local.env", true); // overrides keys from base