API Reference

Complete API documentation for safe-env.

env()

Function Signature

function env<T extends SchemaDefinition>(
  schema: T,
  options?: { env?: EnvSource }
): InferSchema<T>

Parameters

schema(required)

Object mapping environment variable names to DSL strings.

{
  DATABASE_URL: "string:url",
  PORT: "number:default=3000",
}

options(optional)

Configuration object with optional environment source override.

options.env - Custom environment source (defaults to process.env if available)

{
  env: {
    DATABASE_URL: "https://custom-db.com",
    PORT: "8080",
  },
}

Returns

Fully typed object matching your schema. TypeScript infers the correct types:

// Input schema
const ENV = env({
  PORT: "number:default=3000",
  NODE_ENV: "enum:development,production",
});

// TypeScript infers:
// ENV.PORT: number
// ENV.NODE_ENV: "development" | "production"

Throws

EnvValidationError - When validation fails

import { env, EnvValidationError } from "@liahus/safe-env";

try {
  const ENV = env({
    DATABASE_URL: "string:url",
  });
} catch (error) {
  if (error instanceof EnvValidationError) {
    // Handle validation error
    console.error(error.message);
    console.error(error.errors);
  }
}

EnvValidationError

Class Definition

class EnvValidationError extends Error {
  message: string;
  errors: Array<{ key: string; message: string }>;
}

Properties

message

Human-readable error message. Secrets are automatically masked.

"Environment variable validation failed:
  DATABASE_URL: Invalid url (received: undefined)
  JWT_SECRET: String must contain at least 32 character(s) (received: [REDACTED (length: 12)])"

errors

Array of individual validation errors. No secrets exposed.

[
  { key: "DATABASE_URL", message: "Invalid url" },
  { key: "JWT_SECRET", message: "String must contain at least 32 character(s)" },
]

Usage Example

import { env, EnvValidationError } from "@liahus/safe-env";

try {
  const ENV = env({
    DATABASE_URL: "string:url",
    PORT: "number",
    JWT_SECRET: "string:min=32:secret",
  });
} catch (error) {
  if (error instanceof EnvValidationError) {
    // Log the full message
    console.error(error.message);
    
    // Iterate over individual errors
    error.errors.forEach((err) => {
      console.error(`${err.key}: ${err.message}`);
    });
    
    // Exit with error code
    process.exit(1);
  }
}

Type Inference

safe-env provides full TypeScript type inference from your schema:

import { env } from "@liahus/safe-env";

const ENV = env({
  STRING_VAR: "string",
  NUMBER_VAR: "number",
  ENUM_VAR: "enum:val1,val2,val3",
  PORT: "number:default=3000",
});

// TypeScript knows:
// ENV.STRING_VAR: string
// ENV.NUMBER_VAR: number
// ENV.ENUM_VAR: "val1" | "val2" | "val3"
// ENV.PORT: number

// Type errors if you try to use incorrectly:
// ENV.PORT.toUpperCase() // ❌ Error: Property 'toUpperCase' does not exist on type 'number'
// ENV.ENUM_VAR === "invalid" // ❌ Error: This comparison appears to be unintentional

Environment Source

By default, safe-env uses process.env if available. You can override this:

import { env } from "@liahus/safe-env";

// Use process.env (default)
const ENV1 = env({
  PORT: "number:default=3000",
});

// Use custom environment source
const ENV2 = env(
  {
    PORT: "number:default=3000",
  },
  {
    env: {
      PORT: "8080", // Custom value
    },
  }
);

// Useful for testing
const testEnv = env(
  {
    API_KEY: "string:min=10",
  },
  {
    env: {
      API_KEY: "test-api-key-12345",
    },
  }
);