Schema builders

s is a drop-in for Zod’s z that also records a SurrealQL type for each field — use it the way you use z, and each field is a real Zod schema underneath. This page lists every builder and the TYPE it emits in DDL. For the conceptual side, see from schema to DDL; for the full type-by-type matrix with support status, see type mapping.

Primitives

Primitives

s.* SurrealQL TYPE DESCRIPTION
s.string() string A UTF-8 string.
s.boolean() bool A boolean.
s.number() number Any numeric value; use int/float for a specific kind.
s.literal(v) <value> An exact literal value.
s.enum([...]) <a> | <b> | … A union of string literals.

Numbers

s.int, s.float, and friends share Zod’s numeric base; the format decides the SurrealQL type.

Numbers

s.* SurrealQL TYPE DESCRIPTION
s.int() int An integer.
s.float() float A floating-point number.
s.int32() int A 32-bit integer (validated in app).
s.uint32() int An unsigned 32-bit integer (validated in app).
s.bigint() int A bigint, stored as a SurrealDB int.
s.decimal() decimal An arbitrary-precision Decimal (native, no conversion).

Native SurrealDB types

These map to SurrealDB’s built-in types. datetime, uuid, and bytes convert between app and wire values (see codecs); the rest pass through as their SDK class.

Native types

s.* SurrealQL TYPE DESCRIPTION
s.datetime() datetime Date (app) <-> DateTime (wire). Alias: s.date().
s.duration() duration A Duration (native, no conversion).
s.uuid() uuid string (app) <-> Uuid (wire).
s.bytes() bytes Uint8Array; normalizes a wire ArrayBuffer on decode.
s.file() file A FileRef (native, no conversion).
s.geometry(kind) geometry<kind> A Geometry of the given kind (or geometry if omitted).

String formats

Each format is a string that emits the matching ASSERT in DDL, so SurrealDB enforces the rule on write. They fall in two groups.

Backed by a Zod format:

Zod-backed formats

s.* SurrealQL TYPE DESCRIPTION
s.email() string ASSERT string::is_email($value).
s.url() string ASSERT a valid URL.
s.uuid() uuid See native types — uuid, not a plain string.
s.guid() / nanoid() / cuid() / cuid2() / ulid() / xid() / ksuid() string ASSERT the matching id format.
s.ipv4() / ipv6() / cidrv4() / cidrv6() string ASSERT the matching network format.
s.base64() / base64url() / e164() / jwt() / emoji() string ASSERT the matching format.

Plain string plus a string::is_* assertion:

Assertion-backed formats

s.* SurrealQL TYPE DESCRIPTION
s.alpha() / alphanum() / ascii() / numeric() string ASSERT the matching string::is_* check.
s.semver() / hexadecimal() string ASSERT the matching string::is_* check.
s.latitude() / longitude() string ASSERT a valid coordinate.
s.ip() / domain() string ASSERT a valid IP / domain.

Record links

s.* SurrealQL TYPE DESCRIPTION
User.record() record<user> A link to a user record, from the imported table def (carries its id type).
s.recordId([User, Service]) record<user | service> A link to one of several tables.
s.recordId() record A link to any table.

Structural types

Structural types

s.* SurrealQL TYPE DESCRIPTION
s.object({ ... }) object A nested object; each key becomes a DEFINE FIELD.
s.array(el, { max }) array<el> A list; optional max length.
s.set(el) set<el> A set of unique elements.
s.record(k, v) object A map keyed by k with values v.
s.tuple([...]) array A fixed-length, positionally-typed array.
s.union([...]) <a> | <b> A union of types.
s.discriminatedUnion(key, [...]) <a> | <b> A tagged union.
s.intersection(a, b) object Merged object types.
s.lazy(() => ...) (recursive) A self-referential schema.

s.union / s.intersection also have chainable forms: a.or(b) builds a union (a | b) and a.and(b) an intersection. See field methods.

Wrappers and escape hatches

Wrappers & escape hatches

s.* SurrealQL TYPE DESCRIPTION
.optional() option<T> The field may be absent.
.nullable() T | null The field may be null.
.array() array<T> A list of T.
s.any() / s.unknown() any Any value; opts out of a specific type.
s.coerce.* (per base) Coercing variant of a primitive.
s.custom<T>() (app-only) An app-only value with no built-in wire type — pair it with .$surreal(type, codec) to store it.
s.instanceof(Class) (app-only) A class instance — pair it with .$surreal(type, codec) for its wire mapping.

Where to go next

  • Field methods — the $-clauses you chain onto these.
  • Type mapping — every type with support status and notes.
  • DefinersdefineTable, defineRelation, and the rest.