Relations
Overview
Section titled “Overview”Relations define how collections are connected at the database level. They enable Rebase to:
- Render relation picker fields in entity forms
- Resolve related entities when displaying previews
- Generate foreign key constraints in the Drizzle schema
- Support cascade delete/update behaviors
Relations are defined in the relations array of a collection:
const postsCollection: EntityCollection = { slug: "posts", name: "Posts", dbPath: "posts", properties: { title: { type: "string", name: "Title" }, content: { type: "string", name: "Content", multiline: true }, author: { type: "relation", name: "Author", relationName: "author" } }, relations: [ { relationName: "author", target: () => usersCollection, cardinality: "one", localKey: "author_id" } ]};Relation Types
Section titled “Relation Types”One-to-One / Many-to-One
Section titled “One-to-One / Many-to-One”A foreign key on this table points to another table’s primary key.
relations: [ { relationName: "author", target: () => usersCollection, cardinality: "one", // This entity has ONE author direction: "owning", // The FK is on THIS table localKey: "author_id" // Column on the posts table }]This creates: posts.author_id → users.id
One-to-Many (Inverse)
Section titled “One-to-Many (Inverse)”The foreign key is on the target table, pointing back to this entity.
// On the Users collection:relations: [ { relationName: "posts", target: () => postsCollection, cardinality: "many", // This user has MANY posts direction: "inverse", // The FK is on the TARGET table foreignKeyOnTarget: "author_id" // Column on the posts table }]Many-to-Many (Junction Table)
Section titled “Many-to-Many (Junction Table)”Two collections connected through an intermediate junction table.
// On the Users collection:relations: [ { relationName: "roles", target: () => rolesCollection, cardinality: "many", direction: "owning", through: { table: "user_roles", // Junction table name sourceColumn: "user_id", // FK to this collection targetColumn: "role_id" // FK to target collection } }]This creates:
CREATE TABLE user_roles ( user_id INTEGER REFERENCES users(id), role_id INTEGER REFERENCES roles(id), PRIMARY KEY (user_id, role_id));Relation Properties
Section titled “Relation Properties”To render a relation field in a form, add a property with type: "relation":
properties: { author: { type: "relation", name: "Author", relationName: "author", // Must match a relation in the relations array widget: "select" // "select" (dropdown) or "dialog" (full picker) }}Multi-Hop Joins
Section titled “Multi-Hop Joins”For complex relationships that traverse multiple tables, use joinPath:
// Users → Permissions through Rolesrelations: [ { relationName: "permissions", target: () => permissionsCollection, cardinality: "many", joinPath: [ { table: "user_roles", on: { from: "id", to: "user_id" } }, { table: "roles", on: { from: "role_id", to: "id" } }, { table: "role_permissions", on: { from: "id", to: "role_id" } }, { table: "permissions", on: { from: "permission_id", to: "id" } } ] }]Composite Key Joins
Section titled “Composite Key Joins”joinPath: [ { table: "customers", on: { from: ["company_code", "region_id"], // Multiple columns to: ["code", "region_id"] } }]Cascade Rules
Section titled “Cascade Rules”Control what happens when related entities are updated or deleted:
relations: [ { relationName: "author", target: () => usersCollection, cardinality: "one", localKey: "author_id", onDelete: "cascade", // Delete posts when user is deleted onUpdate: "cascade" // Update FK when user ID changes }]| Action | Behavior |
|---|---|
"cascade" | Propagate the change to related rows |
"restrict" | Prevent the operation if related rows exist |
"no action" | Same as restrict (defer to constraint check) |
"set null" | Set the FK column to NULL |
"set default" | Set the FK column to its default value |
Full Relation Interface
Section titled “Full Relation Interface”interface Relation { relationName?: string; target: () => EntityCollection; cardinality: "one" | "many"; direction?: "owning" | "inverse"; inverseRelationName?: string; localKey?: string; foreignKeyOnTarget?: string; through?: { table: string; sourceColumn: string; targetColumn: string; }; joinPath?: JoinStep[]; onUpdate?: "cascade" | "restrict" | "no action" | "set null" | "set default"; onDelete?: "cascade" | "restrict" | "no action" | "set null" | "set default"; overrides?: Partial<EntityCollection>; validation?: { required?: boolean };}Next Steps
Section titled “Next Steps”- Security Rules — Row Level Security
- Properties — Property types reference