Environment
Detect the current runtime environment and branch on it, and auto-load the matching .env files on Node.
envapt detects which environment it's running in and exposes it through the isProduction / isStaging / isDevelopment / isTest helpers, which work on every runtime. On Node, Bun, and Deno it also loads the matching .env files for that environment.
Detecting the environment
The environment is resolved from the first of these keys with a non-missing value, falling back to development:
ENVIRONMENT → ENV → NODE_ENV → MODE → development
An empty value is skipped like an unset key. A whitespace-only value is skipped only under strict mode.
MODE is last because Vite-family browser builds expose import.meta.env.MODE but none of the others, so binding new PortableSource(import.meta.env) lets the same helpers work there. See Browser.
Read it as an enum, or through boolean helpers for the common checks.
.environment; // Environment.; // boolean
.;
.;
.;Environment has four members: Development, Staging, Production, and Test. Matching is case-insensitive:
production, staging, test, and development map to their member. Any other value, or no key set at all, falls
back to Development; set Envapter.debug = 'warn' to log that fallback.
Branching on the environment
Use the helpers where behavior should differ per environment.
const logLevel = . ? 'warn' : 'debug';
if (.) {
// wire up dev-only tooling
}Overriding detection
Set Envapter.environment to pin the environment explicitly, regardless of what the env vars say. It accepts the Environment enum or a string.
. = .;
. = 'production'; // strings work tooAn explicit assignment also selects which .env files load on the next read, so set it before reading environment-specific values.
Pin Envapter.environment once, early in your entry file, before any config reads. Setting it after a read means
that read already used the previously detected environment's files.
Per-environment .env files
The cascade reads files from disk, so it runs on Node, Bun, and Deno only. On Cloudflare Workers and in the browser there is no filesystem and no cascade; you bind an environment source instead. See Sources.
For the detected (or overridden) environment (production, staging, development), envapt layers a dotenv-flow cascade, loading the first file that exists at each level, highest precedence first:
.env.${env}.local.env.${env}.env.local.env
So in production it reads .env.production.local → .env.production → .env.local → .env. Values from higher-precedence files win; missing files are skipped.
Precedence is most-specific-wins: .env.production outranks .env.local. This differs from dotenv-flow /
Next.js, where .env.local sits on top. The intent is that a committed .env.production stays authoritative for
that environment even if a stray .env.local is present.
Custom profiles
When your per-environment files live elsewhere, configureProfiles layers explicit paths on top of the cascade for the environments you name. Earlier paths in the array win. On the portable build these no-op by default, see Compatibility.
.({
[.]: { : ['config/prod.env', 'secrets/prod.env'] },
[.]: { : 'config/staging.env' }
});A path you name in configureProfiles must exist. A missing configured path throws EnvaptError with code
EnvFilesNotFound on the next read. The auto-cascade files are skipped when missing, but configured paths are not.
Set useDefaults: false to load only the configured paths and skip the auto-cascade entirely.
.({
[.]: { : 'config/prod.env' },
: false
});
.(); // back to the pure cascadeTo bypass environment-based selection altogether and load a fixed set of files, set Envapter.envPaths directly, covered in Configuration.
Envapter
Read environment variables as typed values with Envapter's static and instance methods, ordered fallbacks, and fail-fast checks.
Decorators
Bind a class property to an environment variable with the @Envapt decorator. Modern TC39 accessor decorators by default, legacy decorators at envapt/legacy.