Ir al contenido

Relaciones

jsorm usa builders de relaciones explícitos que describen la relación SQL exacta entre dos modelos. Cada builder genera el SQL de join y mutación apropiado sin eager loading oculto ni lazy proxies.

BuilderRelación SQLCuándo usar
t.belongsTo(Model)Foreign key en esta tablaUser tiene columna roleId
t.hasOne(Model)Foreign key en otra tablaProfile tiene columna userId
t.hasMany(Model)Foreign key en otra tablaPost tiene columna authorId
t.manyToMany(Model)Tabla de uniónPosts ↔ Tags via post_tags
import { defineModel, t } from 'jsorm';
const Role = defineModel('roles', {
id: t.number().primary(),
name: t.string().unique(),
});
const Profile = defineModel('profiles', {
id: t.number().primary(),
bio: t.string().optional(),
});
const Tag = defineModel('tags', {
id: t.number().primary(),
name: t.string().unique(),
});
const Post = defineModel('posts', {
id: t.number().primary(),
title: t.string(),
// Nombre de tabla de unión inferido desde config de naming (por defecto: alfabético)
tags: t.manyToMany(Tag),
// O especifica tabla de unión y columnas FK explícitamente:
// tags: t.manyToMany(Tag, {
// through: 'post_tags',
// throughLocalKey: 'post_id',
// throughForeignKey: 'tag_id',
// }),
});
const User = defineModel('users', {
id: t.number().primary(),
name: t.string(),
role: t.belongsTo(Role, {
onUpdate: 'cascade',
onDelete: 'restrict',
constraintName: 'fk_users_role_id', // nombre explícito de restricción FK
}).index(),
profile: t.hasOne(Profile),
posts: t.hasMany(Post),
});

Incluye relaciones en select para cargarlas como objetos anidados:

const users = await jsorm.get(User, {
select: {
id: true,
name: true,
role: { name: true },
profile: { bio: true },
posts: {
title: true,
tags: { name: true },
},
},
});
// Tipado: Array<{ id: number; name: string; role: { name: string }; ... }>

Usa where anidado para filtrar por campos de relaciones:

const admins = await jsorm.get(User, {
select: { name: true },
where: {
role: { name: { eq: 'admin' } },
posts: { title: { contains: 'release' } },
},
});
await jsorm.update(User, {
data: {
role: { connect: 1 },
},
where: { id: 5 },
});
await jsorm.update(User, {
data: {
profile: {
create: { bio: 'Builder from Day 1' },
},
},
where: { id: 5 },
});
await jsorm.update(Post, {
data: {
tags: {
connect: [1, 2, 3],
disconnect: [4],
},
},
where: { id: 10 },
});
await jsorm.insert(User, {
name: 'Alice',
role: { connect: 1 },
profile: {
create: { bio: 'Builder' },
},
posts: {
create: [
{ title: 'First post', tags: { connect: [1, 2] } },
],
},
});

Configura onUpdate y onDelete en belongsTo para controlar la integridad referencial a nivel de base de datos:

role: t.belongsTo(Role, {
onUpdate: 'cascade', // 'cascade' | 'restrict' | 'set-null' | 'no-action'
onDelete: 'restrict',
}),
  1. Usa el builder de relación que coincida con la forma real de los datos — no uses hasMany cuando la relación es manyToMany.
  2. Configura onUpdate y onDelete intencionalmente en lugar de depender de los defaults de la base de datos.
  3. Mantén las mutaciones de relaciones cerca de las operaciones de escritura en lugar de dispersar la lógica de pivot manualmente.
  4. Siempre incluye campos de relaciones explícitamente en select — jsorm nunca carga relaciones implícitamente.