Migration (v4 to v5)
Upgrade to envapt v5, where Converters.array() and envFileOptions replace the old names.
Most of v5 is renames that TypeScript reports once you upgrade. Behavior changes to watch lists the changes that compile cleanly but run differently.
Array converters
Converters.Array and the inline { delimiter, type } object are replaced by the Converters.array(...) builder.
// v4
@Envapt('PORTS', { converter: { delimiter: ',', type: Converters.Number }, fallback: [] })
// v5
@Envapt('PORTS', { converter: Converters.array({ of: Converters.Number }), fallback: [] })of defaults to string, and delimiter defaults to ,. of also accepts a (raw) => T function for a typed element. Converters is now a plain object, so it isn't usable as a type anymore. Use ConverterToken in its place. See Converters.
dotenvConfig is now envFileOptions
Envapter.dotenvConfig is renamed to Envapter.envFileOptions (the type DotenvConfigOptions becomes EnvFileOptions). Only encoding and override remain. quiet, DOTENV_KEY, debug, path, and processEnv are gone.
// v4
Envapter.dotenvConfig = { override: true, quiet: false, debug: true };
// v5
Envapter.envFileOptions = { override: true };To choose which files load, use Envapter.envPaths. envapt doesn't read DOTENV_KEY (dotenv-vault). Decrypt the file yourself, then pass its path to envPaths.
Debug output
The debug option is now Envapter.debug. Output goes to stderr with an [envapt] prefix. v4 printed [dotenv].
// v4
Envapter.dotenvConfig = { debug: true };
// v5
Envapter.debug = 'verbose'; // or set ENVAPT_DEBUG=verboseThe debug levels are silent (default), warn, and verbose, as Debug logging explains.
Validation with Standard Schema
Custom converters that throw still work. v5 also takes a Standard Schema from zod, valibot, arktype, or your own validator, passed as schema.
// v4: a custom converter that throws
@Envapt('PORT', {
converter: (raw) => {
const n = Number(raw);
if (Number.isNaN(n)) throw new Error('bad PORT');
return n;
}
})
// v5
@Envapt('PORT', { schema: z.coerce.number().int().min(1024) })Decorators
The classic positional form is deprecated in v5 and removed in v6. Use the options object. See the v5 to v6 migration.
// v4 (removed in v6)
@Envapt('PORT', 8080, Number)
// v5
@Envapt('PORT', { converter: Converters.Number, fallback: 8080 })Declare a static field as static readonly and an instance field as declare readonly, both with no initializer. v7 later replaced this with the TC39 accessor form, as Migration (v6 to v7) explains. Decorators has the current forms.
Node version
v5 lowered the minimum Node version from >=24 to >=20, since the old minimum existed only for an API envapt no longer uses. Bun >=1.3 and Deno >=2.5 are unchanged.
Behavior changes to watch
These changes compile without edits but run differently from v4. Check them before upgrading.
- Per-environment files auto-load. When
envPathsisn't set, v5 loads a cascade of.env.${env}.local,.env.${env},.env.local, then.env, where${env}is the detected environment. A stray.env.localor.env.productionin your working directory now loads. v4 ignored it. The .env files page has the details. - Array element failures throw. If an element fails its converter, the read throws
EnvaptErrorwith codeArrayElementConversionFailed(206). v4 returned a malformed array without an error. - Time-string fallbacks coerce. With
CACHE_TTLunset,Envapter.getUsing('CACHE_TTL', Converters.Time, '10s')now returns10000milliseconds. v4 returned the string'10s'. A malformed time-string fallback throwsMalformedTimeFallback(105). v4 threw an older, more general code. Errors lists every code.
envapt leaves process.env unchanged by default, as v4 did. If you relied on loaded keys appearing in process.env, opt in with Envapter.syncProcessEnv.