Relationships and Extensions
Relationships connect records across collections. Declare the relationship in the schema first; record links are written separately after the model exists.
This page builds on Collections and CLI Changes.
One-to-many references
The short form:
const posts = collection('posts', {
fields: {
author: bd.reference('users', {
onDelete: 'restrict',
}),
},
})
The fluent form:
const posts = collection('posts', {
fields: {
author: bd
.relation()
.references('users')
.onDelete('RESTRICT')
.required(),
},
})
During createSchema(), a collection-name target is resolved to the target
collection's universalIdentifier. Compilation fails when the target does not
exist in the model.
Deletion behavior
.references(target, onDelete) accepts:
restrictsetNullcascade
The chainable .onDelete() form accepts database-style names:
RESTRICTSET NULLCASCADENO ACTION
Choose the behavior based on what should happen to source records when the target record is deleted.
Many-to-many relationships
const posts = collection('posts', {
fields: {
tags: bd.relation().belongsToMany('tags', {
joinCollectionName: 'post_tags',
}),
},
})
createSchema() generates the junction collection and its source/target
relation fields. The relation builder is virtual on the source collection; the
actual links live in the junction table.
Extend the junction collection
Add fields when the relationship itself carries data:
const projects = collection('projects', {
fields: {
members: bd
.relation()
.belongsToMany('users', {
joinCollectionName: 'project_members',
fields: {
role: bd.enum([
'owner',
'editor',
'viewer',
] as const).required(),
sortOrder: bd.number().default(0),
invitedAt: bd.dateTime().optional(),
},
})
.onDelete('CASCADE'),
},
})
These extension fields compile onto the generated junction collection. Use them for attributes that belong to the connection rather than either record.
Relationship values are not ordinary fields
A relation link does not live inside a record's values object. Seed records
first, then use the separate relations channel:
{
"object": "source-object-uid",
"universalIdentifier": "source-row-uid",
"values": {
"title": "Launch plan"
},
"relations": {
"author": "target-row-uid",
"tags": ["tag-row-uid-1", "tag-row-uid-2"]
}
}
sync-records writes records and resolves links in separate passes, so seed
file order does not have to match relationship order.
For each relation field included in relations, the seed converges that field:
- missing links are added,
- undeclared existing links are removed,
- an empty array clears the field,
- omitted relation fields are left alone.
Relationship limits
- Relation targets in seed files are row
universalIdentifiervalues. - Rows created without a seed identity cannot be targeted by a convergent seed.
MORPH_RELATIONis not supported by the 0.9.50 declaration builder.- Standard BusinessDash objects use their own APIs and business rules.
For the complete record-seeding and readback workflow, continue to Relationship Data.
Next: Unified Form Orchestration.