#Types
Alepha provides schema validation through the t singleton from "alepha". It wraps TypeBox with opinionated defaults: objects reject extra properties, strings have length limits, and arrays cap their size.
#Basic Usage
1import { t } from "alepha";2 3const userSchema = t.object({4 id: t.uuid(),5 email: t.email(),6 name: t.text(),7 age: t.optional(t.integer()),8});
t is a TypeProvider instance. All methods return TypeBox schemas that work with standard TypeBox utilities (Value.Check, Value.Decode, etc.).
#Strings
#t.string() vs t.text()
t.string() creates a raw string with no length limit. Use it for internal values where length is irrelevant.
t.text() adds length limits and text processing. Use it for user input, database fields, and API schemas.
1t.string() // no limits2t.text() // max 255 chars, trimmed3t.text({ size: "short" }) // max 64 chars, trimmed4t.text({ size: "long" }) // max 1024 chars, trimmed5t.text({ size: "rich" }) // max 65535 chars, trimmed
t.text() trims whitespace by default. You can control this and enable lowercase conversion:
1t.text({ trim: false }) // no trimming2t.text({ trim: true, lowercase: true }) // trim + lowercase
#Text Presets
Shorthand methods for common text sizes:
1t.shortText() // same as t.text({ size: "short" }) — 64 chars2t.longText() // same as t.text({ size: "long" }) — 1024 chars3t.richText() // same as t.text({ size: "rich" }) — 65535 chars
#Default Length Limits
These static properties control the defaults. Override them at startup if needed:
1TypeProvider.DEFAULT_STRING_MAX_LENGTH // 2552TypeProvider.DEFAULT_SHORT_STRING_MAX_LENGTH // 643TypeProvider.DEFAULT_LONG_STRING_MAX_LENGTH // 10244TypeProvider.DEFAULT_RICH_STRING_MAX_LENGTH // 65535
1import { TypeProvider } from "alepha/core";2 3TypeProvider.DEFAULT_STRING_MAX_LENGTH = 512;4TypeProvider.DEFAULT_RICH_STRING_MAX_LENGTH = 100000;
#Numbers
1t.number() // any number2t.integer() // integer (no fractional part)3t.int32() // integer clamped to signed 32-bit range (-2147483647 to 2147483647)4t.int64() // JS-safe integer (-9007199254740991 to 9007199254740991)
t.int64() is NOT a true 64-bit integer. JavaScript cannot represent all int64 values. For true int64, use t.bigint() which stores values as strings.
All number methods accept TypeBox number options:
1t.integer({ minimum: 0, maximum: 100 })2t.number({ exclusiveMinimum: 0 })
#Objects
Objects reject additional properties by default:
1const schema = t.object({2 name: t.text(),3 email: t.email(),4});5// { name: "alice", email: "[email protected]", extra: true } → validation error
To allow additional properties:
1t.object({ name: t.text() }, { additionalProperties: true })
#Arrays
Arrays are limited to 1000 items by default:
1t.array(t.string()) // max 1000 items2t.array(t.string(), { maxItems: 50 }) // max 50 items3t.array(t.string(), { minItems: 1 }) // at least 1 item
Override the global default:
1TypeProvider.DEFAULT_ARRAY_MAX_ITEMS = 5000;
#Modifiers
#Optional and Nullable
1t.optional(t.string()) // string | undefined2t.nullable(t.string()) // string | null
These can be combined:
1t.optional(t.nullable(t.string())) // string | null | undefined
#Partial, Pick, Omit
1const user = t.object({2 id: t.uuid(),3 name: t.text(),4 email: t.email(),5});6 7t.partial(user) // all fields optional8t.pick(user, ["id", "name"]) // only id and name9t.omit(user, ["id"]) // name and email only
All three preserve the additionalProperties: false default.
#Extend
Add properties to an existing schema:
1const baseUser = t.object({2 id: t.uuid(),3 name: t.text(),4});5 6const admin = t.extend(baseUser, {7 role: t.const("admin"),8 permissions: t.array(t.text()),9});
t.extend also accepts an array of schemas to merge multiple bases:
1t.extend([baseUser, timestamped], { extra: t.text() })
#Nullify
Maps all properties of an object to nullable:
1const schema = t.object({2 name: t.text(),3 age: t.integer(),4});5 6t.nullify(schema)7// equivalent to: { name: string | null, age: number | null }
#Format Types
#Bigint
String-encoded arbitrary-precision integer:
1t.bigint() // validates "123456789", "-42", etc.
Values are represented as strings to avoid JavaScript number limitations.
#UUID
1t.uuid() // validates UUID format (e.g. "550e8400-e29b-41d4-a716-446655440000")
#URL
1t.url() // validates URL format
#File and Stream
1t.file() // file-like object (browser File API compatible)2t.file({ maxSize: 1048576 }) // with size limit (bytes)3t.stream() // experimental streaming type
#Domain Types
1t.email() // validates email format, auto-trims and lowercases
#Phone (E.164)
1t.e164() // validates E.164 format, e.g. "+1234567890"
#Language Tag (BCP 47)
1t.bcp47() // validates BCP 47 tags, e.g. "en", "en-US", "fr-CA"
#Date and Time
1t.datetime() // ISO 8601 date-time, e.g. "2024-01-15T10:30:00Z"2t.date() // ISO 8601 date, e.g. "2024-01-15"3t.time() // ISO 8601 time, e.g. "10:30:00"4t.duration() // ISO 8601 duration, e.g. "P1DT12H"
#Enums
String enums with built-in validation:
1t.enum(["ACTIVE", "INACTIVE", "BANNED"])2// validates that the value is one of the listed strings
Enum values are validated both by pattern matching and by an enum constraint on the schema.
#Other Types
1t.const("value") // literal value 2t.boolean() // boolean 3t.null() // null 4t.any() // any (no validation) 5t.void() // void 6t.undefined() // undefined 7t.union([...]) // union of schemas 8t.tuple([...]) // fixed-length array 9t.record(k, v) // Record<K, V>10t.json() // Record<string, any> — convenience for JSON blobs
#Validation
Alepha validates data through alepha.codec.validate(). This is the same validation used internally by $action, $env, and other primitives.
1import { Alepha, t } from "alepha"; 2 3const alepha = Alepha.create(); 4const schema = t.object({ 5 name: t.text(), 6 email: t.email(), 7}); 8 9const result = alepha.codec.validate(schema, {10 name: " Alice ",11 email: "[email protected]",12});13// result: { name: "Alice", email: "[email protected]" }
Validation does more than type checking. It applies preprocessing defined by t.text():
- Trimming: strings created with
t.text()are trimmed by default. - Lowercase: strings with
lowercase: true(liket.email()) are lowercased. - Null coercion:
nullvalues in non-nullable fields becomeundefined, which are then stripped from objects.
If validation fails, a TypeBoxError is thrown with details about the first failing constraint.
#Validation Options
1alepha.codec.validate(schema, value, {2 trim: true, // apply text trimming (default: true)3 nullToUndefined: true, // convert null to undefined for non-nullable fields (default: true)4 deleteUndefined: true, // remove undefined keys from objects (default: true)5});
#Encoding
alepha.codec.encode() validates data and serializes it to a target format:
1const schema = t.object({ 2 id: t.uuid(), 3 name: t.text(), 4}); 5 6// Validate and return the cleaned object (default) 7const obj = alepha.codec.encode(schema, data); 8 9// Validate and serialize to JSON string10const json = alepha.codec.encode(schema, data, { as: "string" });11 12// Validate and serialize to binary (for protobuf, msgpack, etc.)13const bytes = alepha.codec.encode(schema, data, { as: "binary" });
You can skip validation with validation: false:
1alepha.codec.encode(schema, data, { validation: false, as: "string" });
#Codec Formats
The default codec is "json". Alepha also ships a "keyless" codec (smaller payloads, used for internal RPC). Additional codecs like Protobuf can be registered:
1alepha.codec.register({2 name: "protobuf",3 codec: myProtobufCodec,4});5 6alepha.codec.encode(schema, data, { encoder: "protobuf", as: "binary" });
#Decoding
alepha.codec.decode() deserializes data and validates it against a schema:
1const result = alepha.codec.decode(schema, jsonString);2// result is validated and typed as Static<typeof schema>
Specify a codec if the data isn't standard JSON:
1const result = alepha.codec.decode(schema, binaryData, { encoder: "protobuf" });
Validation runs automatically after decoding. Disable it with validation: false.
#Accessing TypeBox Directly
If you need raw TypeBox functionality:
1t.raw // the TypeBox Type object
1t.raw.Intersect([schemaA, schemaB])