Skip to main content

Documentation Index

Fetch the complete documentation index at: https://next-safe-env.dev/llms.txt

Use this file to discover all available pages before exploring further.

next-safe-env exports a set of TypeScript types you can use to annotate functions, derive types from your schema, or build wrapper utilities. All types are re-exported from the package root, so you can import them directly from next-safe-env. None of these types have a runtime representation - they exist solely in the TypeScript type system.

ServerOnly<T>

ServerOnly<T> brands a type T with a unique symbol that marks it as originating from the server schema. Every value in the returned env object that comes from a server key carries this brand, making its origin visible in IDE tooltips.
declare const SERVER_ONLY_BRAND: unique symbol

type ServerOnly<T> = T & { readonly [SERVER_ONLY_BRAND]: void }
Because TypeScript uses structural typing, ServerOnly<T> is assignment-compatible with T. It does not produce a compile error by itself. To get a hard compile error when client components import server vars, add import 'server-only' at the top of your env.ts file.
import type { ServerOnly } from 'next-safe-env'

// Annotate a function that only accepts server-side values
function connectDB(url: ServerOnly<string>) { /* ... */ }

ClientEnv<TEnv>

ClientEnv<TEnv> strips every ServerOnly-branded key from an env type, leaving only the client-safe keys. Use it to constrain functions or components that must not access server variables.
type ClientEnv<TEnv extends Record<string, unknown>> = {
  [K in keyof TEnv as TEnv[K] extends { readonly [SERVER_ONLY_BRAND]: void }
    ? never
    : K]: TEnv[K]
}
import type { ClientEnv } from 'next-safe-env'
import { env } from '@/env'

type ClientVars = ClientEnv<typeof env>

// TypeScript errors if you reference a server key here
function renderHeader(vars: ClientVars) {
  return vars.NEXT_PUBLIC_APP_NAME  // ✅ fine
  // vars.DATABASE_URL              // ✗ property does not exist on ClientVars
}

InferEnv<TServer, TClient>

InferEnv<TServer, TClient> is the full return type of createEnv(). Server keys are wrapped in ServerOnly<T>; client keys are their plain inferred types. The whole object is Readonly.
type InferEnv<
  TServer extends Schema | ZodObjectLike<Record<string, unknown>>,
  TClient extends Schema | ZodObjectLike<Record<string, unknown>>,
> = Readonly<
  { [K in keyof InferInput<TServer>]: ServerOnly<InferInput<TServer>[K]> } &
    InferInput<TClient>
>
You rarely need to reference InferEnv directly - the return type of createEnv() is inferred automatically. Use it when you need to pass the env type as a generic argument elsewhere.
import type { InferEnv } from 'next-safe-env'

type MyEnv = InferEnv<typeof serverSchema, typeof clientSchema>

InferSchema<T>

InferSchema<T> maps a Schema (a Record<string, FieldValidator<unknown>>) to the object type its validators produce. It reads the phantom _type property from each FieldValidator to extract the output type.
type InferSchema<T extends Schema> = {
  [K in keyof T]: T[K] extends FieldValidator<infer V> ? V : never
}
import type { InferSchema } from 'next-safe-env'
import { str, url, port } from 'next-safe-env'

const serverSchema = {
  DATABASE_URL: url(),
  PORT:         port().default(3000),
  NODE_ENV:     str().enum(['development', 'production', 'test']),
}

type ServerShape = InferSchema<typeof serverSchema>
// { DATABASE_URL: string; PORT: number; NODE_ENV: 'development' | 'production' | 'test' }

InferInput<T>

InferInput<T> infers the output type from either a native Schema or a ZodObjectLike. For Zod schemas it reads the _output phantom property; for native schemas it delegates to InferSchema<T>.
type InferInput<T> = T extends ZodObjectLike<
  infer O extends Record<string, unknown>
>
  ? O
  : T extends Schema
    ? InferSchema<T>
    : never
This type is used internally by InferEnv to handle both native and Zod schemas uniformly.

EnvConfig<TServer, TClient>

EnvConfig<TServer, TClient> is the shape of the config object passed to createEnv(). Refer to the createEnv() reference for a full description of each field.
type EnvConfig<
  TServer extends Schema | ZodObjectLike<Record<string, unknown>>,
  TClient extends Schema | ZodObjectLike<Record<string, unknown>>,
> = {
  server: TServer
  client: TClient
  runtimeEnv: {
    [K in (
      | (TServer extends ZodObjectLike<infer O extends Record<string, unknown>>
          ? keyof O
          : keyof TServer)
      | (TClient extends ZodObjectLike<infer O extends Record<string, unknown>>
          ? keyof O
          : keyof TClient)
    ) &
      string]?: string | undefined
  }
  adapter?: 'nextjs' | 'node' | 'edge' | 'vite'
  skipValidation?: boolean
  onValidationError?: (error: ValidationErrorShape) => never
}

ValidationFailure

ValidationFailure describes a single failed field produced during validation. An array of these is attached to EnvValidationError and passed to onValidationError.
type ValidationFailure = {
  field: string
  expected: string
  received: string
  message: string
}
field
string
required
The environment variable name that failed, e.g. "DATABASE_URL".
expected
string
required
A human-readable description of what was expected, e.g. "valid URL" or "length >= 32".
received
string
required
The actual value (or "undefined") that was received.
message
string
required
The full error message combining expected and received, e.g. "Expected valid URL. Got: \"postgres-localhost\"".

ValidationStats

ValidationStats provides a summary count of how many variables were checked and how many failed, split by server and client. It is attached to EnvValidationError.
type ValidationStats = {
  serverTotal: number
  clientTotal: number
  serverFailed: number
  clientFailed: number
}
serverTotal
number
required
Total number of keys in the server schema.
clientTotal
number
required
Total number of keys in the client schema.
serverFailed
number
required
Number of server keys that failed validation.
clientFailed
number
required
Number of client keys that failed validation.

FieldValidator<T>

FieldValidator<T> is the interface every validator class implements. The generic T is the TypeScript output type - string, number, boolean, or their | undefined variants. The _type property is a phantom: it never exists at runtime and is only used by InferSchema<T> to extract T.
interface FieldValidator<T = unknown> {
  readonly isOptional: boolean
  readonly defaultValue: T | undefined
  // phantom - satisfies the interface but never assigned at runtime (use `declare`)
  _type: T
  parse(rawValue: unknown, fieldName: string): T
}
isOptional
boolean
required
true when .optional() has been called on the validator.
defaultValue
T | undefined
required
The fallback value set by .default(v), or undefined if no default was configured.
parse
(rawValue: unknown, fieldName: string) => T
required
Parses and validates rawValue. Throws a FieldError if the value is invalid. Called once per field during the validation pass inside createEnv().

Adapter

Adapter is the interface that all built-in adapters (nextjs, node, edge, vite) implement. You can also implement this interface to build a custom adapter.
type Adapter = {
  name: string
  beforeValidate(
    serverSchema: Schema,
    clientSchema: Schema,
    rawEnv: Record<string, string | undefined>,
  ): Record<string, string | undefined>
  afterValidate<T extends Record<string, unknown>>(env: T): T
}
name
string
required
Identifier for the adapter, e.g. "nextjs" or "node".
beforeValidate
function
required
Called before any field is validated. Receives the server schema, client schema, and raw env map. Can throw to block startup (e.g., when a NEXT_PUBLIC_ prefix is missing). Returns the raw env map - possibly filtered - that validation runs against.
afterValidate
function
required
Called after all fields pass validation. Receives the fully-validated env object and returns it, optionally with keys stripped (e.g., the edge adapter removes all non-NEXT_PUBLIC_ keys unconditionally).

Zod interop types

These types are exported for advanced Zod interop use cases. They are duck-typed interfaces - no zod package import is required.

ZodTypeLike<T>

A duck-typed interface matching zod.ZodType. createEnv() detects Zod schemas at runtime using this structural shape.
interface ZodTypeLike<T = unknown> {
  readonly _output: T
  safeParse(
    data: unknown,
  ): { success: true; data: T } | { success: false; error: ZodErrorLike }
}

ZodObjectLike<T>

A duck-typed interface matching zod.ZodObject. Required for the createEnv({ server: z.object({ ... }) }) syntax.
interface ZodObjectLike<
  T extends Record<string, unknown> = Record<string, unknown>,
> extends ZodTypeLike<T> {
  readonly shape: Record<string, ZodTypeLike<unknown>>
}

ZodErrorLike

A duck-typed interface matching the error shape exposed by zod.ZodError.
type ZodErrorLike = {
  readonly errors: ReadonlyArray<{
    readonly path: ReadonlyArray<string | number>
    readonly message: string
  }>
}