env

Total, fallback-safe readers for environment variables that never crash at import time.

The env module provides small, predictable readers for process.env. Each getter is total: it never throws and never returns a value outside the requested range. A missing, blank, malformed, or out-of-range variable falls back to the caller's default, so a bad .env degrades a single feature instead of crashing the process at startup.

The one deliberate exception is envRequired, which exists precisely to fail loudly for credentials.

Values are read on every call rather than cached at module load. This makes the helpers safe to use in tests where process.env changes between assertions. If you need load-time semantics, read the value once into a constant.

Definition

env.ts
export type EnvRangeFallback = "fallback" | "clamp";

export interface EnvNumberOptions {
  min?: number;
  max?: number;
  onRangeError?: EnvRangeFallback;
}

export function envInt(
  name: string,
  fallback: number,
  options?: EnvNumberOptions,
): number;
export function envFloat(
  name: string,
  fallback: number,
  options?: EnvNumberOptions,
): number;
export function envIntOrInfinity(name: string, fallback: number): number;
export function envStr(name: string, fallback: string): string;
export function envList(name: string, fallback?: readonly string[]): string[];
export function envBool(name: string, fallback: boolean): boolean;
export function envRequired(name: string): string;

Range options

envInt and envFloat accept the same options object.

PropertyTypeDefaultDescription
minnumber-InfinityInclusive lower bound.
maxnumberInfinityInclusive upper bound.
onRangeErrorEnvRangeFallback'fallback''fallback' returns the caller's default when the parsed value is out of range. 'clamp' returns the nearest bound instead.

When min is greater than max, the bounds are treated as misconfigured and the fallback is returned regardless of onRangeError.

Use onRangeError: 'clamp' for tuning knobs such as thresholds or concurrency limits. A typo then lands on the nearest valid value instead of silently disabling the feature. Keep the default 'fallback' for values where any substitution would be wrong.

Functions

envInt

Integer reader. Falls back when the variable is missing, empty, non-numeric, non-integer, or out of range.

ParameterTypeRequiredDescription
namestringYesThe environment variable name.
fallbacknumberYesReturned when the value is unusable.
optionsEnvNumberOptionsNoValue range and out-of-range behavior.

" 42 ", 1e3, and 0x10 are accepted because Number understands them. "12.5", "abc", "", and "Infinity" are rejected.

example.ts
import { envInt } from "@riavzon/utils/server";

const port = envInt("PORT", 3000, { min: 1, max: 65535 });
const retries = envInt("RETRIES", 3, { min: 1, onRangeError: "clamp" });

envFloat

Float reader. Falls back when the variable is missing, empty, non-numeric, or out of range.

ParameterTypeRequiredDescription
namestringYesThe environment variable name.
fallbacknumberYesReturned when the value is unusable.
optionsEnvNumberOptionsNoValue range and out-of-range behavior.

NaN and Infinity are rejected here. Use envIntOrInfinity when "unbounded" is itself a meaningful setting.

example.ts
import { envFloat } from "@riavzon/utils/server";

const threshold = envFloat("SIMILARITY_THRESHOLD", 0.9, { min: 0, max: 1 });
const temperature = envFloat("TEMPERATURE", 0.2, {
  min: 0,
  max: 2,
  onRangeError: "clamp",
});

envIntOrInfinity

Positive integer, or Infinity when the variable is explicitly infinity, inf, or none.

ParameterTypeRequiredDescription
namestringYesThe environment variable name.
fallbacknumberYesReturned when the value is unusable.

The sentinels are case-insensitive and surrounding whitespace is ignored. Any other value passes through envInt with min: 1, so 0 and negative numbers fall back instead of reaching a loop as a zero budget.

example.ts
import { envIntOrInfinity } from "@riavzon/utils/server";

// MAX_ITEMS=none means "no limit"
const maxItems = envIntOrInfinity("MAX_ITEMS", 100);
const budget = Number.isFinite(maxItems) ? maxItems : Number.MAX_SAFE_INTEGER;

envStr

String reader with a fallback for missing and blank values.

ParameterTypeRequiredDescription
namestringYesThe environment variable name.
fallbackstringYesReturned when the variable is missing or whitespace-only.

The value is trimmed, but inner whitespace and case are preserved.

example.ts
import { envStr } from "@riavzon/utils/server";

const region = envStr("AWS_REGION", "eu-central-1");
const appName = envStr("APP_NAME", "My App"); // inner spaces kept

envList

Comma-separated list, split, trimmed, and lower-cased, with blanks dropped.

ParameterTypeRequiredDescription
namestringYesThe environment variable name.
fallbackreadonly string[]NoReturned as a copy when the variable is missing or holds only separators. Defaults to [].

"a, B ,,c " becomes ['a', 'b', 'c']. Duplicates are kept, so the order and multiplicity of the raw value survive.

example.ts
import { envList } from "@riavzon/utils/server";

const allowedHosts = envList("ALLOWED_HOSTS"); // [] when unset
const suffixes = envList("TRUSTED_SUFFIXES", ["gov"]); // fallback is not mutated
The returned array is always a fresh copy, including when the fallback is used. Mutating the result never affects the array you passed in.

envBool

Boolean reader with an explicit allow-list.

ParameterTypeRequiredDescription
namestringYesThe environment variable name.
fallbackbooleanYesReturned for missing, blank, or unrecognized values.
ValueResult
true, 1, yes, ontrue
false, 0, no, offfalse
Anything else, including ture or 2fallback

Matching is case-insensitive. A typo falls back rather than guessing, so a mistake cannot silently flip a safety switch.

example.ts
import { envBool } from "@riavzon/utils/server";

const bypassCache = envBool("BYPASS_CACHE", false);
const dryRun = envBool("DRY_RUN", true);

envRequired

Reads a variable that must be present and non-blank.

ParameterTypeRequiredDescription
namestringYesThe environment variable name.

Returns the trimmed value and throws when the variable is missing, empty, or whitespace-only.

example.ts
import { envRequired } from "@riavzon/utils/server";

const databaseUrl = envRequired("DATABASE_URL");
// Throws: Missing required environment variable: DATABASE_URL
This is the only throwing getter in the module. Use it for credentials, connection strings, and anything else where a silent fallback would hide the misconfiguration until the first request arrives. Call it at the point of use rather than at module scope so a missing variable fails with a useful stack trace instead of breaking every import.

Behavior summary

Value in process.envenvIntenvFloatenvStrenvListenvBool
Unsetfallbackfallbackfallbackfallback copyfallback
'' or whitespacefallbackfallbackfallbackfallback copyfallback
'42'4242'42'['42']fallback
'12.5'fallback12.5'12.5'['12.5']fallback
'abc'fallbackfallback'abc'['abc']fallback
'Infinity'fallbackfallback'Infinity'['infinity']fallback
'yes'fallbackfallback'yes'['yes']true
'a, B ,,c 'fallbackfallback'a, B ,,c'['a', 'b', 'c']fallback
Logo