valrs

API Reference

Complete API documentation for valrs

API Reference

This section provides complete documentation for all valrs exports.

The v Namespace

The v namespace is the primary API for valrs, providing a Zod-compatible fluent interface for schema definition and validation.

import { v } from 'valrs';
 
// Create schemas
const userSchema = v.object({
  name: v.string(),
  age: v.number(),
});
 
// Parse and validate
const user = userSchema.parse({ name: 'Alice', age: 30 });
 
// Safe parsing (no exceptions)
const result = userSchema.safeParse(input);
if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.issues);
}
 
// Type inference
type User = v.infer<typeof userSchema>;

Initialization

init()

Initialize the WebAssembly module. Must be called before using any validation functions.

import { init } from 'valrs';
 
await init();

Returns: Promise<void>

Note: In Node.js, this uses a JavaScript fallback and returns immediately. In browsers, the WASM binary is loaded asynchronously.


isInitialized()

Check if the WASM module has been initialized.

import { isInitialized } from 'valrs';
 
if (isInitialized()) {
  // Safe to use validation
}

Returns: boolean


Primitive Schemas

v.string()

Creates a schema for string values with validation and transformation methods.

const nameSchema = v.string();
nameSchema.parse('Alice'); // 'Alice'
nameSchema.parse(123);     // throws ValError

Returns: ValString

String Methods

MethodDescription
.min(length, message?)Minimum length
.max(length, message?)Maximum length
.length(length, message?)Exact length
.email(message?)Valid email format
.url(message?)Valid URL format
.uuid(message?)Valid UUID v4 format
.cuid(message?)Valid CUID format
.cuid2(message?)Valid CUID2 format
.ulid(message?)Valid ULID format
.datetime(message?)Valid ISO 8601 datetime
.ip(message?)Valid IPv4 or IPv6
.regex(pattern, message?)Match regex pattern
.includes(substring, message?)Contains substring
.startsWith(prefix, message?)Starts with prefix
.endsWith(suffix, message?)Ends with suffix
.trim()Trim whitespace (transform)
.toLowerCase()Convert to lowercase (transform)
.toUpperCase()Convert to uppercase (transform)
const emailSchema = v.string().email();
const usernameSchema = v.string().min(3).max(20).trim();

v.number()

Creates a schema for number values (IEEE 754 double-precision).

const ageSchema = v.number();
ageSchema.parse(25);    // 25
ageSchema.parse('25');  // throws ValError

Returns: ValNumber

Number Methods

MethodDescription
.min(value, message?)Minimum value (inclusive)
.max(value, message?)Maximum value (inclusive)
.gt(value, message?)Greater than
.gte(value, message?)Greater than or equal
.lt(value, message?)Less than
.lte(value, message?)Less than or equal
.int(message?)Must be integer
.positive(message?)Must be positive
.nonnegative(message?)Must be non-negative
.negative(message?)Must be negative
.nonpositive(message?)Must be non-positive
.multipleOf(value, message?)Must be divisible by value
.finite(message?)Must be finite
.safe(message?)Must be safe integer
const ageSchema = v.number().int().positive();
const scoreSchema = v.number().min(0).max(100);

v.bigint()

Creates a schema for BigInt values.

const bigSchema = v.bigint();
bigSchema.parse(123n);  // 123n
bigSchema.parse(123);   // throws ValError

Returns: ValBigInt

BigInt Methods

MethodDescription
.min(value, message?)Minimum value
.max(value, message?)Maximum value
.positive(message?)Must be positive
.nonnegative(message?)Must be non-negative
.negative(message?)Must be negative
.nonpositive(message?)Must be non-positive

v.boolean()

Creates a schema for boolean values.

const flagSchema = v.boolean();
flagSchema.parse(true);    // true
flagSchema.parse('true');  // throws ValError

Returns: ValBoolean


v.date()

Creates a schema for Date objects.

const dateSchema = v.date();
dateSchema.parse(new Date());   // Date object
dateSchema.parse('2024-01-01'); // throws ValError

Returns: ValDate

Date Methods

MethodDescription
.min(date, message?)Minimum date
.max(date, message?)Maximum date

v.null()

Creates a schema that only accepts null.

const nullSchema = v.null();
nullSchema.parse(null);      // null
nullSchema.parse(undefined); // throws ValError

Returns: ValNull


v.undefined()

Creates a schema that only accepts undefined.

const undefinedSchema = v.undefined();
undefinedSchema.parse(undefined); // undefined
undefinedSchema.parse(null);      // throws ValError

Returns: ValUndefined


v.void()

Creates a schema that accepts undefined (alias for undefined, for function return types).

const voidSchema = v.void();
voidSchema.parse(undefined); // undefined

Returns: ValVoid


v.any()

Creates a schema that accepts any value. Use sparingly.

const anySchema = v.any();
anySchema.parse('anything'); // 'anything'
anySchema.parse(123);        // 123

Returns: ValAny


v.unknown()

Creates a schema that accepts any value but requires type narrowing before use.

const unknownSchema = v.unknown();
unknownSchema.parse('anything'); // unknown type

Returns: ValUnknown


v.never()

Creates a schema that never matches any value. Useful for exhaustive type checking.

const neverSchema = v.never();
neverSchema.parse(anything); // always throws

Returns: ValNever


Constructor Schemas

v.object()

Creates an object schema from a shape definition.

const User = v.object({
  name: v.string(),
  age: v.number().int().positive(),
  email: v.string().email().optional(),
});
 
type User = v.infer<typeof User>;
// { name: string; age: number; email?: string }
 
const user = User.parse({ name: 'Alice', age: 30 });

Returns: ValObject<T>

Object Methods

MethodDescription
.extend(shape)Add additional properties
.merge(schema)Merge with another object schema
.pick(keys)Select specific properties
.omit(keys)Exclude specific properties
.partial()Make all properties optional
.deepPartial()Make all properties optional recursively
.required()Make all properties required
.passthrough()Allow unknown properties
.strict()Reject unknown properties
.strip()Remove unknown properties
.catchall(schema)Validate unknown properties with schema
.keyof()Get enum of keys
const BaseUser = v.object({
  name: v.string(),
  age: v.number(),
});
 
const ExtendedUser = BaseUser.extend({
  email: v.string().email(),
});
 
const PartialUser = BaseUser.partial();
const NameOnly = BaseUser.pick(['name']);

v.array()

Creates an array schema for the given element type.

const schema = v.array(v.string());
schema.parse(['a', 'b', 'c']); // ['a', 'b', 'c']
 
const withMin = v.array(v.number()).min(1);
withMin.parse([]);     // throws
withMin.parse([1, 2]); // [1, 2]

Returns: ValArray<S>

Array Methods

MethodDescription
.min(length, message?)Minimum array length
.max(length, message?)Maximum array length
.length(length, message?)Exact array length
.nonempty(message?)Array must have at least one item
.elementAccess the element schema

v.tuple()

Creates a tuple schema with fixed element types.

const schema = v.tuple([v.string(), v.number()]);
schema.parse(['hello', 42]); // ['hello', 42]
 
const withRest = v.tuple([v.string()]).rest(v.number());
withRest.parse(['hello', 1, 2, 3]); // ['hello', 1, 2, 3]

Returns: ValTuple<T>

Tuple Methods

MethodDescription
.rest(schema)Allow additional elements of type

v.record()

Creates a record schema (object with string keys and typed values).

// Record<string, string>
const dict = v.record(v.string());
dict.parse({ a: 'hello', b: 'world' }); // OK
 
// Record<string, number> with explicit key schema
const counts = v.record(v.string(), v.number());
counts.parse({ count: 42, score: 100 }); // OK

Signatures:

  • v.record(valueSchema) - String keys with typed values
  • v.record(keySchema, valueSchema) - Typed keys and values

Returns: ValRecord<K, V>


v.map()

Creates a schema for JavaScript Map objects.

const schema = v.map(v.string(), v.number());
schema.parse(new Map([['a', 1], ['b', 2]])); // OK

Returns: ValMap<K, V>


v.set()

Creates a schema for JavaScript Set objects.

const schema = v.set(v.string());
schema.parse(new Set(['a', 'b', 'c'])); // OK

Returns: ValSet<V>


v.union()

Creates a union schema (one of multiple types).

const schema = v.union([v.string(), v.number()]);
schema.parse('hello'); // 'hello'
schema.parse(42);      // 42
schema.parse(true);    // throws

Returns: ValUnion<T>


v.discriminatedUnion()

Creates a discriminated union schema (tagged union) for efficient matching.

const schema = v.discriminatedUnion('type', [
  v.object({ type: v.literal('a'), value: v.string() }),
  v.object({ type: v.literal('b'), count: v.number() }),
]);
 
schema.parse({ type: 'a', value: 'hello' }); // OK
schema.parse({ type: 'b', count: 42 });      // OK
schema.parse({ type: 'c' });                 // throws with helpful error

Returns: ValDiscriminatedUnion<D, T>


v.intersection()

Creates an intersection schema (all types must match).

const A = v.object({ a: v.string() });
const B = v.object({ b: v.number() });
const AB = v.intersection(A, B);
 
AB.parse({ a: 'hello', b: 42 }); // { a: 'hello', b: 42 }

Returns: ValIntersection<L, R>


v.literal()

Creates a schema for a single literal value.

const hello = v.literal('hello');
hello.parse('hello'); // 'hello'
hello.parse('world'); // throws
 
const fortyTwo = v.literal(42);
fortyTwo.parse(42);   // 42
 
type Hello = v.infer<typeof hello>; // 'hello'

Returns: ValLiteralValue<T>


v.enum()

Creates an enum schema for a fixed set of string values.

const Role = v.enum(['admin', 'user', 'guest']);
Role.parse('admin'); // 'admin'
Role.parse('other'); // throws
 
type Role = v.infer<typeof Role>; // 'admin' | 'user' | 'guest'
 
// Access values like an enum
Role.enum.admin; // 'admin'

Returns: ValEnum<T>


v.nativeEnum()

Creates a schema for TypeScript native enums.

enum Status { Active, Inactive }
const schema = v.nativeEnum(Status);
 
schema.parse(Status.Active);   // 0
schema.parse(Status.Inactive); // 1
schema.parse(2);               // throws

Returns: ValNativeEnum<T>


Utility Functions

v.preprocess()

Preprocesses input before passing to the schema.

const schema = v.preprocess(
  (val) => (typeof val === 'string' ? parseInt(val, 10) : val),
  v.number()
);
 
schema.parse('42');  // 42
schema.parse(42);    // 42

Parameters:

  • preprocessFn: (value: unknown) => Input - Transform function
  • schema: ValSchema<Input, Output> - Schema to validate after preprocessing

Returns: ValPreprocessed<Input, Output>


v.coerce

Coercion schema builders that attempt to convert input values to the target type before validation.

v.coerce.string().parse(42);           // '42'
v.coerce.number().parse('42');         // 42
v.coerce.boolean().parse(1);           // true
v.coerce.bigint().parse('123');        // 123n
v.coerce.date().parse('2024-01-01');   // Date object
MethodDescription
v.coerce.string()Coerce to string using String()
v.coerce.number()Coerce to number using Number() (fails if NaN)
v.coerce.boolean()Coerce to boolean using Boolean()
v.coerce.bigint()Coerce to bigint using BigInt()
v.coerce.date()Coerce to Date using new Date()

Streaming Validation

v.stream()

Streams and validates a JSON array with O(1) memory usage.

import { v, stream } from 'valrs';
 
const UserSchema = v.object({
  name: v.string(),
  age: v.number(),
});
 
// Stream from fetch response
const response = await fetch('/users.json');
for await (const user of stream(v.array(UserSchema), response.body!)) {
  console.log(user); // Each user validated as it arrives
}
 
// Collect all items
const users = await stream(v.array(UserSchema), response.body!).toArray();

Parameters:

  • schema: ValArray<S> - Array schema to validate
  • input: StreamInput - ReadableStream, Response, string, or async iterable
  • options?: StreamOptions - Optional streaming configuration

Returns: StreamResult<T> or StreamResultWithErrors<T>

StreamOptions

OptionTypeDescription
maxItemsnumberMaximum items to process
maxBytesnumber | stringMaximum bytes (e.g., '100MB')
timeoutnumber | stringTimeout duration (e.g., '30s')
onError'throw' | 'skip' | 'collect'Error handling strategy
highWaterMarknumberBackpressure threshold (default: 16)

v.streamLines()

Streams and validates NDJSON (newline-delimited JSON) data.

import { v, streamLines } from 'valrs';
 
const LogEntry = v.object({
  timestamp: v.string(),
  level: v.enum(['info', 'warn', 'error']),
  message: v.string(),
});
 
for await (const entry of streamLines(LogEntry, ndjsonStream)) {
  console.log(entry);
}

Parameters:

  • schema: ValSchema<unknown, T> - Schema for each line
  • input: StreamInput - Input stream
  • options?: StreamOptions - Optional streaming configuration

Returns: StreamResult<T>


ValError Class

Error thrown when schema.parse() fails. Contains detailed information about all validation issues.

try {
  v.string().parse(123);
} catch (error) {
  if (error instanceof ValError) {
    console.log(error.issues);
    // [{ code: 'invalid_type', path: [], message: 'Expected string, received number', ... }]
  }
}

Properties

PropertyTypeDescription
issuesReadonlyArray<ValIssue>All validation issues
messagestringFormatted error message
name'ValError'Error name
firstErrorValIssue | undefinedFirst issue, if any

Methods

format()

Formats errors as a nested object matching the path structure.

const error = new ValError([
  { message: 'Required', path: ['user', 'name'], code: 'custom' },
  { message: 'Invalid email', path: ['user', 'email'], code: 'custom' },
]);
 
error.format();
// {
//   _errors: [],
//   user: {
//     _errors: [],
//     name: { _errors: ['Required'] },
//     email: { _errors: ['Invalid email'] },
//   }
// }

Returns: FormattedError<T>


flatten()

Flattens issues into a simple object with form errors and field errors.

error.flatten();
// {
//   formErrors: [],
//   fieldErrors: {
//     'user.name': ['Required'],
//     'user.email': ['Invalid email'],
//   }
// }

Returns: FlattenedError<T>


hasErrorAt(path)

Checks if there are errors at the specified path.

error.hasErrorAt(['user', 'email']); // true
error.hasErrorAt(['items', 0, 'name']); // false

Parameters:

  • path: ReadonlyArray<string | number> - Path to check

Returns: boolean


errorsAt(path)

Gets all errors at the specified path.

const emailErrors = error.errorsAt(['user', 'email']);
// [{ code: 'custom', path: ['user', 'email'], message: 'Invalid email' }]

Parameters:

  • path: ReadonlyArray<string | number> - Path to check

Returns: ReadonlyArray<ValIssue>


addIssue(issue)

Returns a new ValError with an additional issue.

const newError = error.addIssue({
  code: 'custom',
  path: ['field'],
  message: 'Custom error',
});

Returns: ValError


addIssues(issues)

Returns a new ValError with additional issues.

const newError = error.addIssues(otherError.issues);

Returns: ValError


Static Methods

ValError.isValError(value)

Type guard to check if a value is a ValError instance.

if (ValError.isValError(error)) {
  console.log(error.issues);
}

Returns: value is ValError


ValError.fromValIssues(issues)

Creates a ValError directly from ValIssue array.

const error = ValError.fromValIssues([
  { code: 'custom', path: [], message: 'Error' }
]);

Returns: ValError


Issue Codes

All possible Zod-compatible validation error codes:

CodeDescription
invalid_typeType mismatch
invalid_literalLiteral value mismatch
customCustom validation error
invalid_unionNo union variant matched
invalid_union_discriminatorInvalid discriminator value
invalid_enum_valueInvalid enum value
unrecognized_keysUnknown object keys
invalid_argumentsInvalid function arguments
invalid_return_typeInvalid function return
invalid_dateInvalid Date
invalid_stringString format validation failed
too_smallValue too small
too_bigValue too big
invalid_intersection_typesIntersection type mismatch
not_multiple_ofNot a multiple of value
not_finiteNot a finite number

Error Map

Customize error messages globally or per-validation.

setErrorMap()

Sets a global error map for all validations.

import { setErrorMap } from 'valrs';
 
setErrorMap((issue, ctx) => {
  if (issue.code === 'invalid_type') {
    return `Expected ${issue.expected}, got ${issue.received}`;
  }
  return ctx.defaultError;
});

getErrorMap()

Gets the current global error map.

const currentMap = getErrorMap();

resetErrorMap()

Resets the global error map to undefined.

resetErrorMap();

Schema Methods

All schemas inherit these methods from ValSchema:

parse(value)

Validates and returns the value, throwing ValError on failure.

const result = schema.parse(input);

safeParse(value)

Validates and returns a result object (never throws).

const result = schema.safeParse(input);
 
if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.issues);
}

Returns: SafeParseResult<T>


optional()

Makes the schema accept undefined.

const schema = v.string().optional();
type T = v.infer<typeof schema>; // string | undefined

nullable()

Makes the schema accept null.

const schema = v.string().nullable();
type T = v.infer<typeof schema>; // string | null

nullish()

Makes the schema accept null or undefined.

const schema = v.string().nullish();
type T = v.infer<typeof schema>; // string | null | undefined

default(value)

Provides a default value for undefined inputs.

const schema = v.string().default('anonymous');
schema.parse(undefined); // 'anonymous'

catch(value)

Provides a fallback value on validation failure.

const schema = v.number().catch(0);
schema.parse('invalid'); // 0

transform(fn)

Transforms the validated value.

const schema = v.string().transform((s) => s.length);
type T = v.infer<typeof schema>; // number

refine(fn, message?)

Adds a custom validation predicate.

const schema = v.string().refine(
  (s) => s.includes('@'),
  'Must contain @'
);

superRefine(fn)

Adds a custom validation with full context access.

const schema = v.object({
  password: v.string(),
  confirm: v.string(),
}).superRefine((data, ctx) => {
  if (data.password !== data.confirm) {
    ctx.addIssue({
      code: 'custom',
      path: ['confirm'],
      message: 'Passwords must match',
    });
  }
});

pipe(schema)

Chains another schema after this one.

const schema = v.string().pipe(v.coerce.number());

Type Inference

v.infer<T>

Infers the output type from a schema.

const schema = v.object({
  name: v.string(),
  age: v.number(),
});
 
type User = v.infer<typeof schema>;
// { name: string; age: number }

v.input<T>

Infers the input type from a schema (useful when input differs from output).

const schema = v.coerce.number();
type Input = v.input<typeof schema>;   // unknown
type Output = v.output<typeof schema>; // number

v.output<T>

Infers the output type from a schema (alias for v.infer).


Types

See Types Reference for complete type definitions.