Model
Define database tables with the Model class
Model
Models represent database tables. Each model contains fields (columns) and relations (foreign keys).
Creating a Model
Use s.model() with an object of field definitions:
import { s } from "viborm";
const user = s.model({
id: s.string().id().ulid(),
email: s.string().unique(),
name: s.string(),
createdAt: s.dateTime().default(() => new Date()),
});Table Name
By default, VibORM uses the variable name. Use .map() to set a custom table name:
const user = s
.model({
id: s.string().id(),
// ...
})
.map("users"); // SQL: CREATE TABLE "users" (...)Chainable Methods
.map(tableName)
Sets the database table name.
.map("users")
.map("user_accounts").index(fields, options?)
Adds an index on one or more fields.
// Single field index
.index("email")
// Composite index
.index(["lastName", "firstName"])
// With options
.index(["status", "createdAt"], {
name: "idx_status_date",
type: "btree", // btree | hash | gin | gist
unique: false,
where: "status != 'deleted'" // Partial index (PostgreSQL)
}).id(fields, options?)
Defines a compound primary key (when multiple fields form the PK):
const membership = s.model({
orgId: s.string(),
userId: s.string(),
role: s.string(),
}).id(["orgId", "userId"]);
// With custom constraint name
.id(["orgId", "userId"], { name: "membership_pk" }).unique(fields, options?)
Adds a compound unique constraint:
const user = s.model({
id: s.string().id(),
email: s.string(),
orgId: s.string(),
}).unique(["email", "orgId"]);
// With custom constraint name
.unique(["email", "orgId"], { name: "user_email_org_unique" }).extends(fields)
Adds additional fields to an existing model:
const baseModel = s.model({
id: s.string().id(),
createdAt: s.dateTime().default(() => new Date()),
});
const user = baseModel.extends({
email: s.string().unique(),
name: s.string(),
});Index Options
| Option | Type | Description |
|---|---|---|
name | string | Custom index name |
type | "btree" | "hash" | "gin" | "gist" | Index type (PostgreSQL) |
unique | boolean | Unique index |
where | string | Partial index condition |
Complete Example
const user = s
.model({
// Primary key with auto-generation
id: s.string().id().ulid(),
// Required fields
email: s.string().unique(),
passwordHash: s.string(),
// Optional fields
name: s.string().nullable(),
bio: s.string().nullable(),
// With defaults
role: s.enum(["USER", "ADMIN"]).default("USER"),
active: s.boolean().default(true),
createdAt: s.dateTime().default(() => new Date()),
updatedAt: s.dateTime().updatedAt(),
// Relations (config-first, getter-last pattern)
posts: s.relation.oneToMany(() => post),
profile: s.relation.optional().oneToOne(() => profile),
})
.map("users")
.index("email")
.index(["role", "active"])
.unique(["email", "orgId"]);Accessing Model Internals
The ~ property exposes internal state (for advanced use):
const user = s.model({ ... });
user["~"].fields // Field definitions
user["~"].fieldMap // Map<string, Field>
user["~"].relations // Map<string, Relation>
user["~"].tableName // Table name
user["~"].indexes // Index definitions
user["~"].compoundId // Compound PK (if any)
user["~"].schemas // ArkType validation schemas
user["~"].infer // Inferred TypeScript typeType Inference
Access the inferred type for a model:
type User = (typeof user)["~"]["infer"];
// { id: string; email: string; name: string | null; ... }