Relations
Builders
Section titled “Builders”jsorm uses explicit relation builders that describe the exact SQL relationship between two models. Each builder generates the appropriate join and mutation SQL without hidden eager loading or lazy proxies.
Relation builders
Section titled “Relation builders”| Builder | SQL relationship | When to use |
|---|---|---|
t.belongsTo(Model) | Foreign key on this table | User has roleId column |
t.hasOne(Model) | Foreign key on other table | Profile has userId column |
t.hasMany(Model) | Foreign key on other table | Post has authorId column |
t.manyToMany(Model) | Junction table | Posts ↔ Tags via post_tags |
Example
Section titled “Example”import { defineModel, t } from "jsorm";
/* * Use lazy model resolvers (() => Model) * to safely support circular and inverse relations. */
export const Role = defineModel("roles", { id: t.number().primary(), name: t.string().unique(),
// one role → many users users: t.hasMany(() => User),});
export const Profile = defineModel("profiles", { id: t.number().primary(), bio: t.string().optional(),
// one profile → belongs to one user user: t.belongsTo(() => User, { constraintName: "fk_profiles_user_id", onUpdate: "cascade", onDelete: "cascade", }).index(),});
export const Tag = defineModel("tags", { id: t.number().primary(), name: t.string().unique(),
// many tags ↔ many posts posts: t.manyToMany(() => Post),});
export const Post = defineModel("posts", { id: t.number().primary(), title: t.string(),
// many posts ↔ many tags tags: t.manyToMany(() => Tag),
// many posts → one user user: t.belongsTo(() => User, { constraintName: "fk_posts_user_id", onUpdate: "cascade", onDelete: "set null", }).index(),});
export const User = defineModel("users", { id: t.number().primary(), name: t.string(),
// many users → one role role: t.belongsTo(() => Role, { constraintName: "fk_users_role_id", onUpdate: "cascade", onDelete: "restrict", }).index(),
// one user → one profile profile: t.hasOne(() => Profile),
// one user → many posts posts: t.hasMany(() => Post),});Selecting relations
Section titled “Selecting relations”Include relations in select to load them as nested objects:
const users = await jsorm.get(User, { select: { id: true, name: true, role: { name: true }, profile: { bio: true }, posts: { title: true, tags: { name: true }, }, },});// Typed: Array<{ id: number; name: string; role: { name: string }; ... }>Filtering through relations
Section titled “Filtering through relations”Use nested where to filter by relation fields:
const admins = await jsorm.get(User, { select: { name: true }, where: { role: { name: { eq: 'admin' } }, posts: { title: { contains: 'release' } }, },});Relation mutations
Section titled “Relation mutations”Connect to an existing record
Section titled “Connect to an existing record”await jsorm.update(User, { data: { role: { connect: 1 }, }, where: { id: 5 },});Create and connect
Section titled “Create and connect”await jsorm.update(User, { data: { profile: { create: { bio: 'Builder from Day 1' }, }, }, where: { id: 5 },});Many-to-many connect and disconnect
Section titled “Many-to-many connect and disconnect”await jsorm.update(Post, { data: { tags: { connect: [1, 2, 3], disconnect: [4], }, }, where: { id: 10 },});Full example with mixed mutation
Section titled “Full example with mixed mutation”await jsorm.insert(User, { name: 'Alice', role: { connect: 1 }, profile: { create: { bio: 'Builder' }, }, posts: { create: [ { title: 'First post', tags: { connect: [1, 2] } }, ], },});Referential integrity options
Section titled “Referential integrity options”Configure onUpdate and onDelete on belongsTo to control database-level referential integrity:
role: t.belongsTo(Role, { onUpdate: 'cascade', // 'cascade' | 'restrict' | 'set-null' | 'no-action' onDelete: 'restrict',}),Best practices
Section titled “Best practices”- Use the relation builder that matches the real data shape — don’t use
hasManywhen the relationship ismanyToMany. - Configure
onUpdateandonDeleteintentionally rather than relying on database defaults. - Keep relation mutations close to write operations instead of scattering pivot logic manually.
- Always include relation fields explicitly in
select— jsorm never loads relations implicitly.