Data modeling
Beyond the primitives, DORM models arrays, embedded documents, and enums — each with real type inference and recursive validation.
const User = dorm.model("users", {
email: f.string({ required: true, unique: true }), // → unique index
role: f.enum(["admin", "member"]), // typed "admin" | "member"
tags: f.array(f.string()), // string[]
address: f.embedded({ // typed nested object
city: f.string({ required: true }),
zip: f.string({ default: "00000" }),
}),
});Arrays
f.array(element) validates every element against element (which is itself a field builder). Elements are coerced too — an array of f.ref(...) accepts hex strings and stores ObjectIds.
tags: f.array(f.string());
scores: f.array(f.number());
members: f.array(f.ref("users"));Embedded documents
f.embedded(schema) validates a nested object against its own schema — including required checks, defaults, and coercion. The result type is the inferred object; required sub-fields are mandatory on create, defaulted/optional ones may be omitted.
address: f.embedded({
city: f.string({ required: true }),
zip: f.string({ default: "00000" }),
});
await User.objects.create({ email, address: { city: "NYC" } }); // zip → "00000"Enums
f.enum(values) constrains a value to a literal union and validates membership.
role: f.enum(["admin", "member", "guest"]); // typed as "admin" | "member" | "guest"Indexes
Mark fields unique: true or index: true, then create the indexes once after connecting:
const User = dorm.model("users", {
email: f.string({ required: true, unique: true }),
name: f.string({ index: true }),
});
await dorm.connect();
await dorm.ensureIndexes(); // all models
// or a single one:
await User.ensureIndexes();unique fields are also checked app-side on create and update — a duplicate raises a clean validation error (DORM_ERROR_4) instead of a raw driver error. See Lifecycle & transactions.
Populate
populate(...refs) hydrates ref fields, replacing each id with the referenced document (an outer join, so documents with a missing ref are kept):
const [post] = await Post.objects.filter({ id }).populate("author");
post.author; // the full user document, not just an ObjectId