| name | typed-env |
| description | Validate and type process.env environment variables at runtime using createEnvironment with a standard-schema-compatible validator. Supports zod, valibot, and arktype only. Use when setting up environment validation, auto-loading .env files via dotenvx, or generating .env.example placeholder files from a schema shape.
|
| type | core |
| library | @stephansama/typed-env |
| library_version | 0.0.7 |
| sources | ["stephansama/packages:core/typed-env/src/index.ts","stephansama/packages:core/typed-env/README.md"] |
typed-env
Runtime environment validation with full TypeScript inference. createEnvironment wraps a standard-schema object schema and returns helpers for loading, validating, and documenting your environment.
Setup
import * as z from "zod";
import { createEnvironment } from "@stephansama/typed-env";
export const env = createEnvironment(
z.object({
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(1),
}),
true,
);
Call env.validate() once at application startup to fail fast with a descriptive error on missing or invalid values.
Core Patterns
Validate at startup
import * as z from "zod";
import { createEnvironment } from "@stephansama/typed-env";
export const env = createEnvironment(
z.object({
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
}),
true,
);
import { env } from "./env";
const config = await env.validate();
Generate a .env.example file
import { env } from "./env";
await env.generateExample(".env.example");
Run this as a script or CI step to keep .env.example in sync with the schema. Requires zod, valibot, or arktype — the schema's vendor field determines how keys are extracted.
Custom dotenvx options
export const env = createEnvironment(schema, {
path: ".env.production",
override: true,
});
Pass a DotenvConfigOptions object as the second argument instead of true for custom .env file paths, override behaviour, or encryption options.
Load env without auto-loading
export const env = createEnvironment(schema);
env.loadEnv({ path: ".env.local" });
const config = await env.validate();
Common Mistakes
CRITICAL Schema library other than zod, valibot, or arktype
Wrong:
import { Type } from "@sinclair/typebox";
const env = createEnvironment(Type.Object({ KEY: Type.String() }));
await env.generateExample(".env.example");
Correct:
import * as z from "zod";
const env = createEnvironment(z.object({ KEY: z.string() }));
await env.generateExample(".env.example");
generateExample uses a hard-coded vendor switch on schema["~standard"].vendor. Only "zod", "valibot", and "arktype" are handled. Any other standard-schema implementation throws.
Source: core/typed-env/src/index.ts:getObjectFromSchema
HIGH Not passing loadEnvironmentConfig — .env values missing
Wrong:
const env = createEnvironment(schema);
await env.validate();
Correct:
const env = createEnvironment(schema, true);
await env.validate();
createEnvironment does not load any .env file by default. validate() reads process.env as-is. Pass true or a DotenvConfigOptions object as the second argument to auto-load via dotenvx.
Source: core/typed-env/src/index.ts:createEnvironment
MEDIUM validate() throws raw JSON on failure
Wrong:
const config = await env.validate();
Correct:
try {
const config = await env.validate();
} catch (err) {
console.error("Environment validation failed:", (err as Error).message);
process.exit(1);
}
On validation failure, validate() throws new Error(JSON.stringify(issues)). The message is machine-readable JSON. Wrap in try/catch at startup and exit explicitly with a human-readable message.
Source: core/typed-env/src/index.ts:validate
See also: typed-templates/SKILL.md — same vendor restriction applies.