Skip to content

Dotenv

A small, zero-dependency .env loader for Node.js. Reads KEY=VALUE lines, coerces values to typed primitives when they look like one (number, boolean, null), supports ${VAR} interpolation between keys, picks the right file based on NODE_ENV, and layers a .env.shared file of defaults underneath.

Highlighted features

Typed primitives, not strings

env(“APP_PORT”, 3000) returns the number 3000 — not the string “3000”. Same for booleans and null.

${VAR} interpolation

DB_URL=postgres://${DB_USER}:${DB_PASS}@${DB_HOST}/db — substitution happens at parse time, references resolve from already-loaded keys.

NODE_ENV file picker

Reads .env.<NODE_ENV> automatically and layers .env.shared underneath for cross-environment defaults.

Zero deps, one file

One source file (src/index.ts), no runtime or peer dependencies. Node-only — uses fs and process.

Install

Terminal window
npm install @mongez/dotenv

Zero runtime / peer dependencies. Node-only.

Quick peek

import { loadEnv, env } from "@mongez/dotenv";
loadEnv();
const port: number = env("APP_PORT", 3000); // 3000, not "3000"
const debug: boolean = env("DEBUG", false); // true / false, not "true"
const dbUrl: string = env("DB_URL");

Boot once at process start, then read typed values from anywhere. env() returns real primitives, not the stringified process.env form.

Mental model

ConceptWhere it livesWhat it is
Internal storeModule-level envData objectThe typed view of every loaded key. Read via env() / env.all().
process.env mirrorReal Node process.envOptional write-through, controlled by override. Always stores strings.
Initial snapshotModule-level initialProcessEnvDataCaptured at first import. resetEnv restores keys from this snapshot.

The loader is stateful and module-scoped. One store per Node process. Calling loadEnv twice merges the second file’s keys into the first store rather than starting over.

Scope boundaries

ConcernLives inWhy
Validation / schemazod, valibot, your codeDoesn’t type-check loaded values
Higher-level config (groups, defaults, dot-notation)@mongez/configThis is one slice — the file-loading slice
Browser/cookie/localStorageOther packagesThis is a Node filesystem reader

Quirks worth knowing

  1. process.env always stringifies. Even though the package writes process.env.PORT = 3000, Node’s process.env setter coerces to "3000". Use env() for the typed value.
  2. ${VAR} is parse-time only. Substitutions happen the moment the value is parsed. Later updates to the referenced key do not re-trigger substitution in earlier lines.

Where to go next

  • LoaderloadEnv / loadEnvFile, file-picking semantics
  • ParserparseLine / parseValue, type coercion rules
  • Recipes — common patterns