v8.1.0

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:

ENVIRONMENTENVNODE_ENVMODEdevelopment

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
EnvironmentMethods.environment: Environment

Get the current application environment

Set the application environment. Accepts either Environment enum or string value.

.; // boolean .; .; .;
NOTE

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';
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 too

An explicit assignment also selects which .env files load on the next read, so set it before reading environment-specific values.

TIP

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

NOTE

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:

  1. .env.${env}.local
  2. .env.${env}
  3. .env.local
  4. .env

So in production it reads .env.production.local.env.production.env.local.env. Values from higher-precedence files win; missing files are skipped.

WARNING

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' }
});
DANGER

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 cascade

To bypass environment-based selection altogether and load a fixed set of files, set Envapter.envPaths directly, covered in Configuration.

On this page