Modelos que generan tipos.
Queries que llegan tipadas.
Defines tu modelo una vez; jsorm gen genera los tipos y el runtime; y consultas con una API declarativa que compila a AST y SQL según el provider. El resultado llega tipado —relaciones incluidas— en Node, en el browser y en el edge.
Playground — del modelo al resultado
lp.playground.model.blurb
1
import { defineModel, t, r } from "@jsorm/core";
2
3
export const User = defineModel("users", {
4
db: "main",
5
fields: {
6
id: t.uuid().primary().autoCreate(),
7
name: t.string(100),
8
email: t.string(255).unique(),
9
active: t.boolean().default(true),
10
createdAt: t.dateTime().autoCreate(),
11
updatedAt: t.dateTime().autoUpdate(),
12
},
13
relations: {
14
posts: r.hasMany("post"),
15
role: r.belongsTo("role"),
16
roles: r.manyToMany("role", {
17
through: "user_roles",
18
extra: { role: t.string(50) },
19
}),
20
},
21
});
Modelo definido → AST → Provider → Base de datos → Resultado tipado
Por qué JSORM
No es un query builder más: es una capa que borra el trabajo repetitivo entre tu esquema, tus tipos y tu base de datos.
Developer experience
El flujo es corto y siempre el mismo: escribes el modelo, corresjsorm gen(o lo dejas en watch) y el editor ya sabe todo lo demás.
pnpm dlx @jsorm/cli configure
pnpm add @jsorm/client @jsorm/core
pnpm add -D @jsorm/cli
pnpm jsorm gen
// → .jsorm/types.ts + runtime files
// → jsorm.users.get() ya está tipadowatch mode
pnpm jsorm watch
// regenera tipos al guardar el modeloCore capabilities
Todo lo que necesitas para modelar y consultar, con la API real de JSORM.
email: t.string(255).unique(),
role: t.enum(["admin", "user"]),
meta: t.json().nullable(),
await jsorm.users.first({
select: { name: { as: "Nombre" } },
where: { email: "juan@test.com" },
});
roles: r.manyToMany("role", {
through: "user_roles",
extra: { role: t.string(50) },
}),
await jsorm.users.get();
await jsorm.cache.sessions.get();
await jsorm.analytics.event.get();
pnpm jsorm configure
pnpm jsorm gen
pnpm jsorm watch
--provider sqlite-node
--provider postgres-node
--provider indexeddb
Antes vs. con JSORM
La misma tarea: traer usuarios activos con sus roles, tipados.
1
interface UserRow {
2
id: string;
3
name: string;
4
role_name: string | null;
5
}
6
7
const rows = await db.query<UserRow>(
8
`SELECT u.id, u.name, r.name AS role_name
9
FROM users u
10
LEFT JOIN user_roles ur ON ur.user_id = u.id
11
LEFT JOIN roles r ON r.id = ur.role_id
12
WHERE u.active = $1
13
ORDER BY u.created_at DESC`,
14
[true],
15
);
16
17
const users = groupRows(rows) as User[]; // cast manual
- Esquema duplicado en SQL y en TypeScript
- Joins y agrupado escritos a mano en cada query
- El cast oculta errores hasta runtime
1
const { data: users } = await jsorm.users.get({
2
select: { id: true, name: true, roles: { name: true } },
3
where: { active: true },
4
orderBy: { createdAt: "desc" },
5
});
- Una definición como fuente de verdad
- Relaciones resueltas por la relación declarada
- Tipo exacto inferido del select, sin casts
Empieza en un comando
El asistente configura provider, naming y rutas. Después solo defines modelos y consultas.
pnpm dlx @jsorm/cli configure