Packages
@thexjs/env
Type-safe environment variable validation for x apps. Define a schema once, get parsed and typed values back, and fail fast with a clear error if something is missing or malformed.
bun add @thexjs/env
Quick start
env.ts
import { createEnv, str, num, bool, oneOf, url } from "@thexjs/env";export const env = createEnv({ server: { DATABASE_URL: url(), PORT: num(), NODE_ENV: oneOf(["development", "production", "test"]), }, client: { NEXT_PUBLIC_API_URL: url(), }, clientPrefix: "NEXT_PUBLIC_", runtimeEnv: process.env,});env.DATABASE_URL; // string, validated as a URLenv.PORT; // numberenv.NODE_ENV; // "development" | "production" | "test"If any variable is missing or fails validation, createEnv throws a single error listing every failure:
validation error
Environment validation failed: server.DATABASE_URL: Expected a valid URL, got "not-a-url" server.PORT: Expected a number, got undefinedValidators
built-in validators
Validator Accepts Notes──────────────────────────────────────────────────────────────────────str() any non-undefined stringnum() numeric strings rejects NaNbool() "true"/"1" → true, "false"/"0" → falseoneOf([...values]) one of the given string literals narrows return typeurl() string parseable by new URL(...)Custom validators
Each validator is { parse(input: string | undefined): T } — write your own for anything not covered:
custom validator
import type { EnvValidator } from "@thexjs/env";function json<T>(): EnvValidator<T> { return { parse(input) { if (input === undefined) throw new Error("Expected JSON, got undefined"); return JSON.parse(input) as T; }, };}client / clientPrefix
The client schema is for variables safe to expose to the browser. clientPrefix enforces that every client key starts with that prefix (e.g. NEXT_PUBLIC_, PUBLIC_) — a key that does not match fails validation, so you cannot accidentally leak a server-only variable through the client schema.
Notes
- Every field is currently required — no built-in optional() or default(). For optional vars, read from process.env directly or write a validator with a fallback.
- runtimeEnv is passed explicitly so this works on Bun, in bundlers that inline process.env.* at build time, or in tests with a mocked env object.