JSON-first ORM for TypeScript

TypeScript ORM.
No decorators. No magic.

Define your schema once with plain TypeScript and get full type inference automatically. Persistent Rust engine in Node, fetch-safe transport on edge — zero codegen required.

terminal
pnpm dlx @jsorm/core init
Zero decorators No codegen Edge-safe Full type inference
Query DSL

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.

src/schema/main/user.ts
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>;
Core features

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
Architecture

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.

Node runtime
JSON query
type-safe DSL
Normalize AST
validated + hashed
Version AST
engine contract
Rust worker
persistent · SHA-256 verified
SQL + params
compiled plan
pg / mysql / sqlite
native adapter
Bun runtime
JSON query
type-safe DSL
Normalize AST
validated + hashed
Version AST
engine contract
Bun native adapter
detects Bun · bypasses Rust worker
SQL + params
compiled plan
sqlite / mysql / pg
Bun built-in DB API
Edge runtime
JSON query
type-safe DSL
Normalize AST
validated + hashed
Version AST
engine contract
@jsorm/fetch
HTTP transport
jsorm HTTP server
Node endpoint (deploy/CI)
SQL → Database
pg / mysql / sqlite

@jsorm/node required on CI/deploy — not included in the edge bundle

Packages

Install only what you use

Each package has a single job. Ship exactly the surface area your runtime needs.

Authoring

@jsorm/core
Authoring

Main authoring package for models, query APIs, config helpers, migration primitives, and compatibility exports.

Node · Edge · CI/CD
$ pnpm add @jsorm/core

Runtime

@jsorm/runtime
Runtime

Runtime-safe package for universal and edge-safe app code when filesystem access or native binaries are unavailable.

Cloudflare Workers · Vercel Edge · Deno · Bun
$ pnpm add @jsorm/runtime

Tooling

@jsorm/node
Tooling

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.

Node.js · Bun · CI/CD · Deploy jobs · Migration jobs
$ pnpm add @jsorm/node

Transport

@jsorm/fetch
Transport

Edge-safe HTTP/fetch transport for runtimes that cannot open TCP sockets or spawn native binaries.

Cloudflare Workers · Vercel Edge · any fetch-only runtime
$ pnpm add @jsorm/fetch

Adapters

@jsorm/pg
Adapters

Native PostgreSQL adapter for Node runtimes, deploy jobs, and direct connection workflows.

PostgreSQL · Node.js
$ pnpm add @jsorm/pg
@jsorm/mysql
Adapters

Native MySQL adapter for the same Node runtime and CLI deployment model.

MySQL · Node.js
$ pnpm add @jsorm/mysql
@jsorm/sqlite
Adapters

Native SQLite adapter for local tooling, embedded workflows, and Node-based releases.

SQLite · Local dev · Embedded · Node.js
$ pnpm add @jsorm/sqlite
Get started

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