@eryx/schema Module

JSON

@module @eryx/schema

Runtime schema construction, validation, and parsing.

This module is the public entrypoint for value schemas. It is designed for runtime data boundaries: request bodies, config files, CLI input, persisted payloads, plugin data, and any other dynamic values that should be validated before your application logic depends on them.

Why schemas instead of ad-hoc checks?

Without schemas, validation logic tends to spread across call sites and diverge over time. With schemas, you define the contract once and reuse it:

parse vs validate

Every schema provides two runtime entrypoints:

validate is for pass/fail checks. parse is for pass/fail plus normalized output.

Use parse when:

Use validate when:

Quick Start

local schema = require("@eryx/schema")

local User = schema.table({
	id = schema.number():int():positive(),
	name = schema.string():trimWhitespace():minLen(1),
	email = schema.optional(schema.string()),
})

local ok, parsedOrErr = User:parse({
	id = 7,
	name = "  Ada  ",
})
-- ok == true
-- parsedOrErr.name == "Ada"

Available Constructors

schema.literal(value)

Matches exactly one literal value (string/number/boolean/nil).

Good for:

local Kind = schema.literal("user")

schema.boolean()

Validates booleans.

Good for:

schema.number()

Validates numbers and supports chainable numeric constraints.

Common constraints:

local Port = schema.number():int():gte(1):lte(65535)

schema.string()

Validates strings and supports both validation and parse-time mutation.

Common validators:

Common mutators:

Use mutators when you want canonicalized output from parse. Use validators when you want strict input acceptance criteria.

schema.array(childSchema)

Validates list-like tables where each element must satisfy a child schema.

Common constraints:

local Tags = schema.array(schema.string():trimWhitespace()):nonempty()

schema.optional(childSchema)

Wraps a schema so nil is accepted.

You can provide a default at parse time:

local RetryCount = schema.optional(schema.number():int()):default(3)

schema.anyOf(...)

Union schema. Parsing succeeds if any branch succeeds. Branches can be schemas or raw literals.

local Role = schema.anyOf("admin", "member", "guest")

schema.map(keySchema, valueSchema)

Validates key/value tables by validating both keys and values. Both key and value schemas can be full schemas or literals.

Good for:

Table Schemas

schema.table({...}) creates object-like schemas with named fields.

Default behavior is strict:

local Config = schema.table({
	host = schema.string(),
	port = schema.number():int(),
	tls = schema.optional(schema.boolean()):default(false),
})

Struct Helpers (@eryx/schema/struct)

Table schemas are created here with schema.table(...). Table transformation helpers (strict/strip/passthrough/catchall/extend/etc) are provided by @eryx/schema/struct.

local Base = schema.table({ a = schema.number() })

local struct = require("@eryx/schema/struct")
local Strict = struct.strict(Base)
local Strip = struct.strip(Base)
local Pass = struct.passthrough(Base)

Common helpers from @eryx/schema/struct:

When to use these helpers:

Full Integration Example

local schema = require("@eryx/schema")

local struct = require("@eryx/schema/struct")
local Payload = struct.strip(schema.table({
	id = schema.number():int(),
	role = schema.anyOf("admin", "member"),
	tags = schema.optional(schema.array(schema.string())),
}))

local ok, result = Payload:parse({
	id = 42,
	role = "admin",
	tags = { "ops" },
	extra = "ignored",
})

Notes and Conventions

Summary

Functions

schema.literal<T>(literal: ((T & (string | number | boolean)) | "__MAGIC_USED_FOR_IMPLICIT_LITERAL_VALUES_NARROWING__")?)LiteralSchema<T>
schema.number(metadata: NumberMetadata?)NumberSchema
schema.string(metadata: StringMetadata?)StringSchema
schema.boolean<T>()BooleanSchema
schema.array<T>(of: t.Schema<T>, metadata: ArrayMetadata<T>?)ArraySchema<T>
schema.table<T>(fields: T & { [string]: t.Schema<any> }, options: TableOptions?)TableSchema<t.stripIndexer<T>>
schema.optional<T>(child: t.Schema<T>, options: { hasDefault: boolean, defaultValue: T? }?)OptionalSchema<T>
schema.anyOf<T>(...: T | t.Schema<any>)UnionSchema<t.extractChildrenFromAnyOfUnion<T>>
schema.map<T, U>(key: T | t.Schema<any>, value: U | t.Schema<any>)MapSchema<t.extractSchemaType<T>, t.extractSchemaType<U>>

API Reference

Functions

schema.literal

Create a schema that matches exactly one literal value. Typical usage is in union branches and tagged object fields, for example kind = schema.literal("user").

schema.literal<T>(literal: ((T & (string | number | boolean)) | "__MAGIC_USED_FOR_IMPLICIT_LITERAL_VALUES_NARROWING__")?)LiteralSchema<T>

Parameters

literal: ((T & (string | number | boolean)) | "__MAGIC_USED_FOR_IMPLICIT_LITERAL_VALUES_NARROWING__")?

Exact value this schema must match.

Returns

LiteralSchema<T>

A literal schema.

schema.number

Create a number schema. By default, this schema accepts any runtime number and applies no extra constraints until modifiers are chained.

schema.number(metadata: NumberMetadata?)NumberSchema

Parameters

metadata: NumberMetadata?

Internal metadata snapshot used when deriving chained schemas.

Returns

A number schema.

schema.string

Create a string schema. By default, this accepts any runtime string and applies no additional constraints until modifiers are chained.

schema.string(metadata: StringMetadata?)StringSchema

Parameters

metadata: StringMetadata?

Internal metadata snapshot used when deriving chained schemas.

Returns

A string schema.

schema.boolean

Create a schema that accepts boolean values. The returned schema is intentionally minimal and composes cleanly inside table, map, optional, and union schemas.

schema.boolean<T>()BooleanSchema

Returns

BooleanSchema

A boolean schema.

schema.array

Create an array schema for values validated by a child schema. Child schemas can be primitive or composed; nested errors are path-prefixed using array indices (for example #2).

schema.array<T>(of: t.Schema<T>, metadata: ArrayMetadata<T>?)ArraySchema<T>

Parameters

of: t.Schema<T>

Child schema applied to each numeric-indexed element.

metadata: ArrayMetadata<T>?

Internal metadata snapshot used for chained schema derivation.

Returns

An array schema.

schema.table

Create a table schema from field definitions. Each key maps to a child schema. By default, unknown keys are rejected unless policy is changed via struct helpers.

schema.table<T>(fields: T & { [string]: t.Schema<any> }, options: TableOptions?)TableSchema<t.stripIndexer<T>>

Parameters

fields: T & { [string]: t.Schema<any> }

Field map where each key points to a child schema.

options: TableOptions?

Optional unknown-key/catchall/readonly configuration.

Returns

TableSchema<t.stripIndexer<T>>

A table schema.

schema.optional

Create an optional schema around a child schema. Use this to model fields that may be absent. For predictable downstream shapes, pair with :default(...).

schema.optional<T>(child: t.Schema<T>, options: { hasDefault: boolean, defaultValue: T? }?)OptionalSchema<T>

Parameters

child: t.Schema<T>

Schema used when input is not nil.

options: { hasDefault: boolean, defaultValue: T? }?

Internal optional metadata, including default configuration.

Returns

An optional schema.

schema.anyOf

Create a union schema that succeeds when any child schema matches. Raw literal values are promoted to literal schemas. This allows concise declarations like schema.anyOf("a", "b", 3). At least one branch is required.

schema.anyOf<T>(...: T | t.Schema<any>)UnionSchema<t.extractChildrenFromAnyOfUnion<T>>

Returns

UnionSchema<t.extractChildrenFromAnyOfUnion<T>>

A union schema.

schema.map

Create a map schema from key and value schemas (or literal shorthands). Literal key/value inputs are promoted automatically, allowing concise forms like schema.map("status", "ok") when strict constants are desired.

schema.map<T, U>(key: T | t.Schema<any>, value: U | t.Schema<any>)MapSchema<t.extractSchemaType<T>, t.extractSchemaType<U>>

Parameters

key: T | t.Schema<any>

Schema (or literal shorthand) used to validate map keys.

value: U | t.Schema<any>

Schema (or literal shorthand) used to validate map values.

Returns

MapSchema<t.extractSchemaType<T>, t.extractSchemaType<U>>

A map schema.