Zod
Validate environment variables with your existing Zod schemas.
The Zod schemas you already write for other inputs can validate the environment too. ArkEnv runs them against that environment and coerces string values before they hit Zod.
Zod 4.2+ embeds
Standard JSON Schema on schema
objects, which is what makes "3000" and "true" coerce. Classic Zod
uses the root @arkenv/standard import. There is no
@arkenv/standard/zod subpath. @arkenv/standard/valibot and
@arkenv/standard/zod-mini exist only to bind JSON Schema converters
those libraries keep off the value.
How the two engines differ is in Validators.
Installation
Install @arkenv/standard next to zod. @arkenv/standard has no
runtime dependencies. Zod stays your dependency.
npm install @arkenv/standard zodDefine your schema
Put the Zod schemas you already write into env.ts. Each field is that
schema. Import { env } where the process starts.
import from "@arkenv/standard";
import * as from "zod";
export const = ({
: .(),
: .().(0).(65535).(3000),
: .().(false),
: .(.()).([]),
:
.(["development", "production", "test"])
.("development"),
});Because Zod 4.2+ includes JSON Schema metadata, ArkEnv automatically converts
"3000" to 3000, "true" to true, and comma-separated lists like "web, api"
into string arrays before validation. You can use standard z.number() and
z.boolean() directly without manual z.coerce helpers.
Defaults and optionals
Zod methods for defaults and optional fields behave consistently with ArkEnv:
| Method | Behavior |
|---|---|
.default(...) | Fallback value used when the environment variable is missing |
.optional() | Allows the environment variable to remain undefined |
emptyAsUndefined: true | Converts empty strings ("") to undefined so .default() triggers |
Zod Mini
On Zod 4.2+ classic?
Skip this section. The root import already coerces. Zod Mini is the build that leaves JSON Schema off the schema object.
Zod Mini (import * as z from "zod/mini")
omits embedded JSON Schema metadata from schema instances. Import
@arkenv/standard/zod-mini so Mini z.number() and z.boolean()
coerce without a callback:
import { } from "@arkenv/standard/zod-mini";
import * as from "zod/mini";
export const = ({
: .(),
: .(),
});TypeScript must use moduleResolution: "bundler" | "node16" | "nodenext".
The root toJsonSchema callback remains available for mixed maps; see
Coercion.
Zod v3
Zod v3 (including zod/v3 subpath exports in Zod 4) implements Standard Schema
validation but lacks native JSON Schema metadata. You can enable automatic
coercion by passing zod-to-json-schema
through the toJsonSchema callback:
import from "@arkenv/standard";
import { } from "zod/v3";
import { } from "zod-to-json-schema";
export const = (
{
: .(),
: .(),
},
{
: (schema) =>
(schema as ., {
: "none",
}),
},
);Use Zod 4.2+ when you can. It ships JSON Schema metadata, so you skip
the toJsonSchema callback.