Skip to content

Schema Options

Every key in the schema object maps to a configuration describing the expected environment variable. Here's a full reference of available options.

Scalar Variables

OptionTypeDescription
type"string" | "number" | "boolean"The expected data type
requiredboolean | (env) => booleanFail if the variable is missing. Pass a function for conditional required
defaultstring | number | booleanFallback when the variable is unset
choicesreadonly (string | number | boolean)[]Fixed set of allowed values (exclusive with validate/format)
validate(value) => booleanCustom validation function (exclusive with choices/format)
format"url" | "email" | "ip" | "port" | "uuid"Built-in format preset for strings (exclusive with choices/validate)
describestringHuman-readable description, shown in error messages
sensitivebooleanRedact the value in error messages and change-listener arguments
coerce(raw: string) => unknownCustom coercion function, runs before type parsing

Array Variables

OptionTypeDescription
type"array"Declares the variable as an array
itemType"string" | "number" | "boolean"The type of each element
separatorstringDelimiter (defaults to ",")
requiredboolean | (env) => booleanFail if the variable is missing; supports a function
defaultstring[]Fallback when the variable is unset
describestringHuman-readable description, shown in error messages
sensitivebooleanRedact the value in error messages and change events
coerce(raw: string) => unknownCustom coercion, bypasses split-by-separator logic

Mutual Exclusivity

choices, validate, and format are mutually exclusive — TypeScript will error if you try to combine them on the same variable, and a runtime check guards against it for plain JavaScript consumers.

ts
// ✅ OK — only one of the three
PORT: { type: "number", required: true, validate: (v) => v >= 1 && v <= 65535 }
NODE_ENV: { type: "string", required: true, choices: ["development", "production"] as const }
API_URL: { type: "string", required: true, format: "url" }

// ❌ Compile error — cannot combine them
PORT: { type: "number", required: true, choices: [3000], validate: (v) => v > 0 }

createEnv Options

The second argument to createEnv configures global behaviour:

OptionTypeDescription
envFilesboolean | string[]Load .env files before validation
prefixstringPrefix prepended when reading each env variable
onError(errors: string[]) => voidCustom error handler — replaces the default throw
strictbooleanProxy that throws on access to keys not in the schema
freezebooleanObject.freeze the result — mutations throw
watchtrueReturn a watchable object with refresh(), on(), off()
validate(env: InferEnv<S>) => boolean | stringCross-field validation after per-field checks pass

WARNING

freeze and watch are mutually exclusive — both at the type level and at runtime.

Released under the MIT License.