Schema DSL
The s.* namespace is nookdb’s schema DSL. Every type, validator, and index in your app starts here.
Shipped builders
import { s } from 'nookdb';
const schema = { users: s.collection({ id: s.id(), // UUID v7, monotonic email: s.string().email(), // RFC 5322 validation name: s.string().min(1).max(100), age: s.number().int().min(0).optional(), // optional → undefined permitted role: s.enum(['admin', 'user']).default('user'), tags: s.array(s.string()), createdAt: s.date().default(() => new Date()), }),};Available builders
s.id()— UUID v7 string. Auto-generated on insert if you don’t supply one.s.string()— UTF-8 string. Chain.min(n),.max(n),.email(),.regex(/…/),.nullable(),.optional(),.default(value | () => value).s.number()— JS number. Chain.int(),.min(n),.max(n),.nullable(),.optional(),.default(...).s.boolean()— Chain.nullable(),.optional(),.default(true | false).s.date()— JSDate. Chain.nullable(),.optional(),.default(...).s.enum([...] as const)— string literal union. The literal types flow through to$type, not juststring. Chain.default(...).s.array(itemBuilder)— homogeneous array of any primitive builder (string / number / boolean / date / enum / nested array).s.collection({...})— wraps a field record into a collection. Chain.index('field'),.index(['a', 'b']),.uniqueIndex('field').
Indexes
const schema = { posts: s .collection({ /* fields */ }) .index('authorId') // single-field .index(['authorId', 'publishedAt']) // composite (prefix-usable) .uniqueIndex('slug'), // unique + index};Indexes are declared on the schema — there is no runtime createIndex. Adding or removing an index triggers a schema-version bump that the migration ledger tracks.
Type inference
type User = typeof schema.users.$type;// { id: string; email: string; name: string; age?: number | undefined; role: 'admin' | 'user'; ... }Use $type in app code to keep handwritten types and runtime validation in lockstep. The Rust core re-validates every write against the same descriptor; if the TS type says role: 'admin' | 'user' then Rust will reject role: 'mod' even when TS lies.
Not yet shipped
These appear in early PRDs and design discussions but are not callable in v0.x — listed here so you don’t reach for them and find a TypeError:
s.object({...})— nested struct fields. Currentlys.collectionis the only structured-record builder; nesting is post-1.0 work (# v2insrc/schema/s.ts).s.ref(() => schema.users)— typed foreign-key references. The same query-time joining can be done by storing the parent id ass.string()and querying both collections; the typed-ref ergonomics are post-1.0.{ sparse: true }index option — null-skipping indexes. Currently all indexes include null values; declare the field.nullable()and filter== nullrows out of your queries.
When these land they’ll be in the changelog. The shipped surface above is stable across v0.x.