Main authoring package for models, query APIs, config helpers, migration primitives, and compatibility exports.
Queries that feel like TypeScript
From schema definition to queries: a JSON-first API with full type inference, relation loading, filtering, and two pagination modes.
import { defineModel, t } from "@jsorm/core";import type { InferModel, InferInput } from "@jsorm/core"; const Role = defineModel("roles", { id: t.number().primary(), name: t.string().unique(),}); const User = defineModel("users", { id: t.number().primary(), name: t.string(), email: t.string().optional(), active: t.boolean().default(true), createdAt: t.date(), role: t.belongsTo(Role),}); // Types inferred — no manual definitions neededtype UserRecord = InferModel<typeof User>;type UserInput = InferInput<typeof User>; Everything you need, nothing you don’t
jsorm is fully instance-based, runtime-safe, and ships zero implicit globals.
Schema-first TypeScript definition
Define your table, fields, defaults, and constraints once with defineModel() and t.*. No decorators, no classes — just plain TypeScript that compiles cleanly with full editor support.
import { defineModel, t } from '@jsorm/core';
const User = defineModel('users', {
id: t.number().primary(),
name: t.string(),
email: t.string().optional(),
active: t.boolean().default(true),
createdAt: t.date(),
role: t.belongsTo(Role),
}); Zero-effort type inference
TypeScript types for query results and insert inputs are inferred automatically from your schema. Primary keys are excluded from input. Defaults make fields optional. No manual type definitions needed.
import type { InferModel, InferInput } from '@jsorm/core';
type UserRecord = InferModel<typeof User>;
// { id: number; name: string; email?: string; active: boolean }
type UserInput = InferInput<typeof User>;
// { name: string; email?: string; roleId: number } All four relation kinds, explicit
Define relationships with a consistent API: belongsTo, hasOne, hasMany, and manyToMany. Use the lazy factory form () => Model for circular or cross-module relations to avoid TypeScript circular-reference errors.
const Post = defineModel('posts', {
id: t.number().primary(),
title: t.string(),
// lazy factory — safe for circular deps
author: t.belongsTo((): typeof User => User),
tags: t.manyToMany(() => Tag),
}); Persistent Rust execution engine
In Node runtimes, queries compile through a long-lived Rust worker — not a fresh process per request. The binary is SHA-256 verified before first use, and AST contracts are version-handshaked. Compiled queries are cached with a 512-entry bounded in-memory store. Edge runtimes use @jsorm/runtime + @jsorm/fetch instead.
const db = await createJsorm().init();
// Worker starts once. Reused for every query.
// Cold: JSON → AST → engine.compile() → SQL
// Hot: signature hit → cached SQL → adapter
await db.close(); // drain pools + shut down worker Multi-database runtime scoping
Connect to multiple databases in one runtime instance. Switch context with db.use(name) — returns a lightweight scoped wrapper without creating new adapter instances or mutating the default scope.
// jsorm.config.ts
connectionSources: { main, analytics, cache }
// app.ts — scoped, no new adapter instances
const report = await db
.use('analytics')
.get(User, { select: { id: true } }); Optional migrations + CLI bootstrap
Migrations are entirely opt-in — connect to an existing database with no migration setup required. When you need them, jsorm init bootstraps the full project and jsorm migrate:generate diffs your models to produce reviewed, editable migration files.
# Bootstrap once — generates config + schema files
pnpm dlx @jsorm/core init
# Generate migration from model diff (reviewed before apply)
jsorm migrate:generate
# Deploy safely in CI — dry-run, strict, verbose
jsorm deploy --dry-run --strict One DSL. Two runtime paths.
Every query starts from the same type-safe JSON DSL. In Node runtimes it compiles through a persistent Rust worker; on Bun it uses native Bun database APIs directly; on Edge it travels over HTTP via @jsorm/fetch.
@jsorm/node required on CI/deploy — not included in the edge bundle
@jsorm/node required on CI/deploy — not included in the edge bundle
Install only what you use
Each package has a single job. Ship exactly the surface area your runtime needs.
Authoring
— Schema, types, query API — used in every environment.Runtime
— Universal + edge-safe runtime layer without native binaries.Runtime-safe package for universal and edge-safe app code when filesystem access or native binaries are unavailable.
Tooling
— CLI, migrations, deploy jobs — Node-only, never bundled to the edge.Runtime and tooling package for Node and Bun. On Node, provides the Rust engine bridge and native adapter plumbing for migrations and deploy. On Bun, detects Bun config and uses Bun native SQLite, MySQL, and PostgreSQL APIs directly — no Rust engine required.
Transport
— HTTP fetch adapter for edge runtimes that cannot open TCP sockets.Edge-safe HTTP/fetch transport for runtimes that cannot open TCP sockets or spawn native binaries.
Adapters
— Native database adapters for Node — pg, mysql, and sqlite.Native PostgreSQL adapter for Node runtimes, deploy jobs, and direct connection workflows.
Native MySQL adapter for the same Node runtime and CLI deployment model.
Native SQLite adapter for local tooling, embedded workflows, and Node-based releases.
One command to bootstrap
jsorm init provisions the engine binary, generates your schema, and writes a ready-to-edit jsorm.config.ts.
pnpm dlx @jsorm/core init