Quick Start
Install envapt and read your first typed environment variable.
Install
pnpm add envaptRead a value
Put a variable in your .env:
PORT=8080Read it as a number, with a fallback for when it is unset:
const port = .('PORT', 3000);The fallback supplies the default and removes undefined from the type. See Envapter for the full reader API and Converters for non-primitive types.
Or bind it to a class
The @Envapt decorator binds a value to a typed field:
class {
@('PORT', { : ., : 3000 })
declare static readonly : number;
}Decorators need experimentalDecorators enabled, and the property declared with declare and no initializer:
{
"compilerOptions": {
"experimentalDecorators": true
}
}Declare the property with declare and no initializer. A real field declaration (or an initializer) emits an
assignment that overwrites envapt's getter, so the property reads back as undefined at runtime. See
Decorators.
Drop-in for dotenv
For the dotenv/config workflow, import envapt/config. It loads the .env cascade and mirrors every loaded key into process.env in one step, so process.env.PORT works without touching Envapter.
import 'envapt/config';
const = ..; // the loaded keys are now on process.envIt is the side-effect form of Envapter.syncProcessEnv = true plus an eager load, and follows the same collision rules. Unlike dotenv/config, it loads the whole per-environment cascade, not a single .env.
Preload it without editing your entry file:
node --import envapt/config app.js # ESM
node -r envapt/config app.js # CommonJSFor typed reads on top, keep using Envapter.
Requirements
envapt runs on Node >=20, Bun >=1.3, and Deno >=2.5. The functional API needs no build step on any runtime; the decorator API is TypeScript and needs compilation, with one caveat on Bun. See Compatibility.