Relationships
Relationships are the core differentiator of @biref/scanner. For every foreign key, the assembler emits two Relationship objects: one outbound on the FK holder and one inbound on the FK target.
Direction
type RelationshipDirection = 'outbound' | 'inbound';
interface Relationship {
direction: RelationshipDirection;
reference: Reference;
}
- outbound: this entity holds the FK column(s) pointing to another entity
- inbound: another entity's FK column(s) point to this entity
Reference
The underlying Reference describes the FK constraint:
interface Reference {
name: string; // constraint name
fromEntity: EntityRef; // { namespace, name } of the FK holder
fromFields: readonly string[]; // FK columns on the source
toEntity: EntityRef; // { namespace, name } of the FK target
toFields: readonly string[]; // referenced columns on the target
confidence: number; // 1 for declared FKs
onUpdate: ReferentialAction | null;
onDelete: ReferentialAction | null;
}
Querying relationships
// All relationships involving users (both directions)
model.relationshipsOf('public', 'users');
// Only outbound (FKs users declares)
model.outboundRelationshipsOf('public', 'users');
// Only inbound (other tables pointing to users)
const incoming = model.inboundRelationshipsOf('public', 'users');
for (const rel of incoming) {
const from = rel.reference.fromEntity;
console.log(`${from.namespace}.${from.name} -> users`);
}
Patterns
One-to-many
orders.user_id -> users.id produces:
outboundonorders(points tousers)inboundonusers(fromorders)
Self-referential
employees.manager_id -> employees.id produces:
outboundonemployees(manager_id -> id)inboundonemployees(from itself)
Both directions appear on the same entity.
Many-to-many (join table)
product_tags(product_id, tag_id) with two FKs produces:
- 2
outboundonproduct_tags(toproductsand totags) - 1
inboundonproducts(fromproduct_tags) - 1
inboundontags(fromproduct_tags)
Cross-name FK
customers.kyc_id -> kyc.id -- the FK column name (kyc_id) differs from the target table name (kyc). The scanner handles this correctly:
const ref = outbound[0].reference;
ref.fromFields // ['kyc_id']
ref.toEntity // { namespace: 'mydb', name: 'kyc' }
ref.toFields // ['id']
Composite FK
returns(order_id, variant_id) -> order_items(order_id, variant_id):
ref.fromFields // ['order_id', 'variant_id']
ref.toFields // ['order_id', 'variant_id']
Column order is preserved.