Ir al contenido

Modelos

Los modelos son la única fuente de verdad en jsorm. Una definición de modelo controla tanto los tipos TypeScript como el comportamiento de queries en runtime.

import type { InferInput, InferModel } from 'jsorm';
import { defineModel, t } from 'jsorm';
const User = defineModel('users', {
id: t.number().primary(),
name: t.string(),
email: t.string().optional(),
active: t.boolean().default(true),
createdAt: t.date(),
score: t.number().nullable(),
});
type UserRecord = InferModel<typeof User>;
// {
// id: number;
// name: string;
// email?: string;
// active: boolean;
// createdAt: Date;
// score: number | null;
// }
type UserInput = InferInput<typeof User>;
// {
// name: string;
// email?: string;
// active?: boolean;
// createdAt: Date;
// score?: number | null;
// }
// Nota: claves primarias y campos de relación excluidos de InferInput; columnas FK se mantienen
BuilderTipo TypeScriptNotas
t.string()stringEquivalente a VARCHAR
t.text()stringTexto sin límite / columna TEXT
t.number()numberNumérico / FLOAT
t.boolean()boolean
t.date()DateSolo fecha
t.time()stringCadena de tiempo HH:MM:SS
t.dateTime()DateTimestamp completo
t.uuid()stringCadena UUID
t.autoIncrement()numberEntero auto-incremental
t.enum(['a', 'b'] as const)'a' | 'b'Unión de strings, verificada en runtime
t.json()unknownAlmacenado como JSON, retornado como objeto
t.belongsTo(Model)ModelRecordFK → padre
t.hasOne(Model)ModelRecordInverso de belongsTo
t.hasMany(Model)ModelRecord[]One-to-many
t.manyToMany(Model)ModelRecord[]Many-to-many vía tabla junction
t.string()
.optional() // undefined permitido; excluido de input requerido
.nullable() // null permitido en tipo y DB
.primary() // marca como clave primaria (excluido de InferInput)
.default('unknown') // valor por defecto; hace el input opcional
.unique() // constraint UNIQUE en migraciones
.index() // INDEX en migraciones
const Post = defineModel('posts', {
id: t.autoIncrement().primary(),
title: t.string(),
slug: t.string().unique(),
body: t.text().optional(),
status: t.enum(['draft', 'published', 'archived'] as const).default('draft'),
published: t.boolean().default(false),
viewCount: t.number().default(0),
publishedAt: t.dateTime().nullable(),
deletedAt: t.date().nullable(),
metadata: t.json(),
});
const Role = defineModel('roles', {
id: t.number().primary(),
name: t.string().unique(),
});
const User = defineModel('users', {
id: t.number().primary(),
name: t.string(),
// belongsTo: columna FK en esta tabla
role: t.belongsTo(Role, {
onUpdate: 'cascade',
onDelete: 'restrict',
constraintName: 'fk_users_role_id', // nombre explícito para migraciones
}).index(),
});
const Tag = defineModel('tags', {
id: t.number().primary(),
name: t.string(),
});
const Article = defineModel('articles', {
id: t.number().primary(),
title: t.string(),
tags: t.manyToMany(Tag),
author: t.belongsTo(User),
});

Para proyectos grandes, co-ubica los modelos cerca de los límites de dominio:

src/
schema/
main/
user.ts ← defineModel('users', {...})
role.ts
posts/
article.ts
tag.ts
index.ts ← re-exporta todos los modelos
jsorm.config.ts ← define connectionSources, migrationSources, models
  1. Mantén los nombres de tabla estables y explícitos — renombra solo a través de migraciones.
  2. Usa t.autoIncrement().primary() para PKs enteros auto-incrementales.
  3. Usa t.uuid() para claves primarias UUID.
  4. Prefiere InferModel / InferInput inferidos en lugar de interfaces manuales.
  5. Un modelo por archivo en proyectos grandes; co-ubica modelos pequeños relacionados.
  6. Agrega constraintName explícito en belongsTo cuando necesites nombres predecibles en migraciones.