Relations
Relations Overview
Define relationships between models
Relations
Relations define how models connect to each other. VibORM supports all standard relationship types.
Relation Builder Pattern
VibORM uses a config-first, getter-last pattern for defining relations:
s.relation.config().type(() => targetModel)This pattern avoids TypeScript circular reference issues because:
- Config methods (
fields,references, etc.) returnthis- no generics involved - Terminal methods (
oneToOne,manyToOne, etc.) introduce the generic last - Since there's no more chaining after the terminal method, TypeScript doesn't need to resolve the generic to enable further method calls
Relation Types
| Relation | Description | Example |
|---|---|---|
s.relation.oneToOne() | One record relates to one other | User → Profile |
s.relation.oneToMany() | One record relates to many | User → Posts |
s.relation.manyToOne() | Many records relate to one | Post → Author |
s.relation.manyToMany() | Many relate to many | Post ↔ Tags |
Quick Example
import { s } from "viborm";
const user = s.model({
id: s.string().id().ulid(),
email: s.string().unique(),
profile: s.relation.optional().oneToOne(() => profile),
posts: s.relation.oneToMany(() => post),
}).map("users");
const profile = s.model({
id: s.string().id().ulid(),
bio: s.string(),
userId: s.string().unique(),
user: s.relation
.fields("userId")
.references("id")
.oneToOne(() => user),
}).map("profiles");
const post = s.model({
id: s.string().id().ulid(),
title: s.string(),
authorId: s.string(),
author: s.relation
.fields("authorId")
.references("id")
.manyToOne(() => user),
tags: s.relation.manyToMany(() => tag),
}).map("posts");
const tag = s.model({
id: s.string().id().ulid(),
name: s.string().unique(),
posts: s.relation.manyToMany(() => post),
}).map("tags");Config Methods (before terminal)
All config methods can be chained before the terminal method:
s.relation
.fields("fieldName") // FK field on current model
.references("fieldName") // Referenced field on target model
.onDelete("cascade") // Referential action on delete
.onUpdate("cascade") // Referential action on update
.optional() // Mark as optional (to-one only)
.through("tableName") // Junction table (many-to-many only)
.A("fieldName") // Junction FK for source (many-to-many only)
.B("fieldName") // Junction FK for target (many-to-many only)
.manyToOne(() => target) // Terminal method - ALWAYS LASTReferential Actions
| Action | Description |
|---|---|
cascade | Delete/update related records |
setNull | Set FK to NULL (requires nullable FK) |
restrict | Prevent delete/update if related records exist |
noAction | Database default behavior |
Class Hierarchy
VibORM uses specialized classes for different relation types:
Relation (abstract base)
├── ToOneRelation → oneToOne, manyToOne (has .optional())
├── OneToManyRelation → oneToMany
└── ManyToManyRelation → manyToMany (has .through(), .A(), .B())This ensures only valid methods appear in IntelliSense.