or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

custom-validators.mderror-handling.mdindex.mdmiddleware.mdvalidation.mdvalidators.md
tile.json

custom-validators.mddocs/

Custom Validators

Factory functions for creating custom validators with type safety and validation logic for domain-specific requirements. These functions enable extending envalid with application-specific validation rules.

Capabilities

Base Validator Factory

Creates custom validators with subtype narrowing capabilities, allowing choices array for constraining valid values.

/**
 * Creates custom validators with subtype narrowing
 * @param parseFn - Function that parses and validates input string
 * @returns BaseValidator function that can be used with specs
 */
function makeValidator<BaseT>(
  parseFn: (input: string) => BaseT
): BaseValidator<BaseT>;

Usage Examples:

import { makeValidator, cleanEnv } from "envalid";

// Create a custom validator for positive integers
const positiveInt = makeValidator((input: string) => {
  const parsed = parseInt(input, 10);
  if (isNaN(parsed) || parsed <= 0) {
    throw new Error("Must be a positive integer");
  }
  return parsed;
});

// Create a custom validator for comma-separated strings
const csvList = makeValidator((input: string) => {
  return input.split(",").map(item => item.trim());
});

// Use custom validators
const env = cleanEnv(process.env, {
  MAX_RETRIES: positiveInt({ default: 3 }),
  ALLOWED_ORIGINS: csvList(),
  WORKER_COUNT: positiveInt({ choices: [1, 2, 4, 8] }),
});

Exact Validator Factory

Creates validators with exact type output, providing no subtype narrowing where output type is exactly T.

/**
 * Creates validators with exact type output
 * No subtype narrowing, output type is exactly T
 * @param parseFn - Function that parses input string to exact type T
 * @returns ExactValidator function for the specified type
 */
function makeExactValidator<T>(
  parseFn: (input: string) => T
): ExactValidator<T>;

Usage Examples:

import { makeExactValidator, cleanEnv } from "envalid";

// Create exact validator for specific enum values
enum LogLevel {
  DEBUG = "debug",
  INFO = "info", 
  WARN = "warn",
  ERROR = "error"
}

const logLevel = makeExactValidator<LogLevel>((input: string) => {
  if (!Object.values(LogLevel).includes(input as LogLevel)) {
    throw new Error(`Invalid log level: ${input}`);
  }
  return input as LogLevel;
});

// Create exact validator for date objects
const dateValidator = makeExactValidator<Date>((input: string) => {
  const date = new Date(input);
  if (isNaN(date.getTime())) {
    throw new Error("Invalid date format");
  }
  return date;
});

// Use exact validators
const env = cleanEnv(process.env, {
  LOG_LEVEL: logLevel({ default: LogLevel.INFO }),
  CREATED_AT: dateValidator(),
});

Structured Validator Factory

Creates validators for complex/structured data, used for JSON and other structured input formats.

/**
 * Creates validators for complex/structured data
 * Used for JSON and other structured input formats
 * @param parseFn - Function that parses input string to unknown type
 * @returns StructuredValidator that can be typed with generics
 */
function makeStructuredValidator(
  parseFn: (input: string) => unknown
): StructuredValidator;

Usage Examples:

import { makeStructuredValidator, cleanEnv } from "envalid";

// Create validator for YAML-like configuration
const yamlConfig = makeStructuredValidator((input: string) => {
  // Simple YAML-like parser (in real usage, use a proper YAML library)
  const lines = input.split('\n');
  const config: Record<string, any> = {};
  
  for (const line of lines) {
    const [key, value] = line.split(':').map(s => s.trim());
    if (key && value) {
      config[key] = isNaN(Number(value)) ? value : Number(value);
    }
  }
  
  return config;
});

// Create validator for custom serialized objects
const serializedUser = makeStructuredValidator((input: string) => {
  const parts = input.split('|');
  if (parts.length !== 3) {
    throw new Error("Invalid user format");
  }
  
  return {
    id: parseInt(parts[0]),
    name: parts[1],
    email: parts[2]
  };
});

interface UserConfig {
  id: number;
  name: string;
  email: string;
}

// Use structured validators
const env = cleanEnv(process.env, {
  APP_CONFIG: yamlConfig<Record<string, any>>(),
  DEFAULT_USER: serializedUser<UserConfig>(),
});

Advanced Custom Validator Patterns

Validator with Complex Validation

import { makeValidator, cleanEnv } from "envalid";

// Custom validator with multiple validation steps
const securePassword = makeValidator((input: string) => {
  if (input.length < 8) {
    throw new Error("Password must be at least 8 characters");
  }
  
  if (!/[A-Z]/.test(input)) {
    throw new Error("Password must contain uppercase letter");
  }
  
  if (!/[a-z]/.test(input)) {
    throw new Error("Password must contain lowercase letter");
  }
  
  if (!/\d/.test(input)) {
    throw new Error("Password must contain a number");
  }
  
  if (!/[!@#$%^&*]/.test(input)) {
    throw new Error("Password must contain special character");
  }
  
  return input;
});

const env = cleanEnv(process.env, {
  ADMIN_PASSWORD: securePassword(),
});

Validator with External Dependencies

import { makeValidator, cleanEnv } from "envalid";
import { isValidObjectId } from "mongodb";

// Validator that uses external library
const mongoId = makeValidator((input: string) => {
  if (!isValidObjectId(input)) {
    throw new Error("Invalid MongoDB ObjectId");
  }
  return input;
});

const env = cleanEnv(process.env, {
  USER_ID: mongoId(),
  DOCUMENT_ID: mongoId(),
});

Validator Type Interfaces

type BaseValidator<BaseT> = <T extends BaseT = BaseT>(
  spec?: Spec<T>
) => ValidatorSpec<T>;

type ExactValidator<T> = (spec?: Spec<T>) => ValidatorSpec<T>;

type StructuredValidator = <T = unknown>(
  spec?: Spec<T>
) => ValidatorSpec<T>;

type ValidatorSpec<T> = RequiredValidatorSpec<T> | OptionalValidatorSpec<T>;

interface RequiredValidatorSpec<T> extends Spec<T> {
  _parse: (input: string) => T;
}

interface OptionalValidatorSpec<T> extends Spec<T | undefined> {
  _parse: (input: string) => T;
}