Runs on Bun and Node.js. Install with:
bun add @rekkr/orm # Bun 1.4.2+ npm install @rekkr/orm # Node.js 24.21+On Node.js, add
pg,mysql2orioredisfor the servers you use β see Installation.
An Eloquent-inspired ORM for Bun and Node.js. On Bun it runs on the native bun:sql client with zero runtime dependencies; on Node.js it runs on the built-in node:sqlite and the standard pg, mysql2 and ioredis drivers. It supports SQLite, MySQL, and PostgreSQL with full TypeScript typing, a chainable query builder, schema migrations, model observers, polymorphic relations, and an interactive REPL.
- π₯ Bun-native, Node.js-ready β
bun:sqlon Bun;node:sqlite,pgandmysql2on Node.js, held to the same results by one test suite - πͺΆ Zero runtime dependencies β On Bun, nothing beyond Bun itself; on Node.js, only the drivers for the databases you use
- π¦ Multi-database β SQLite, MySQL, and PostgreSQL support
- π§― Portable unique errors β Duplicate unique and primary keys share one safe public error type across drivers
- π· Fully Typed β Generate declarations for plain
extends Modelclasses with typed attributes, queries, relations, and eager loads - ποΈ Schema Builder β Programmatic table creation, indexes, foreign keys
- π Query Builder β Chainable joins, unions, relation and pivot shortcuts, SQL diagnostics, and Laravel-style aliases
- β‘ Predictable direct JSON β
rawJson()skips per-row hydration or reports why it cannot - π Search β PostgreSQL full-text search and SQLite FTS5 engines, no search service to run
- π§ Tagged Cache β Redis-backed cache facade, query
remember(), and exact tag invalidation - π£ Events β Application-level event dispatcher with function listeners and class handlers
- π¨ Queue Jobs β Database- and Redis-backed background job queue with named queues, retries, delays, and an
orm queueworker - π οΈ Commands β Artisan-style CLI commands with a signature DSL, argument/option parsing, and
orm run - 𧬠Eloquent-style Models β Property attributes, casts, scopes, soft deletes, and concurrency-safe find-or-create helpers
- π§Ί Collections β Laravel-style paging, filtering, partitioning, strict matching, and aggregation helpers
- π Relations β Standard, many-to-many, polymorphic, through, one-of-many, and relation queries
- ποΈ Observers β Lifecycle hooks (
creating,created,updating,updated, etc.) - π Migrations & CLI β Create, run, reset, refresh, and inspect migrations from the command line
- π± Seeders & Factories β Root or targeted seeders plus typed factories for bulk data and relationship graphs
- π¬ REPL β Inspect models and run queries interactively with
orm repl - β‘ Streaming β
chunk,chunkById,cursor,each,eachById, andlazyfor memory-efficient large dataset processing - π’ Multi-tenant β Database-per-tenant, schema-per-tenant, and RLS strategies with
DB.tenant()andTenantContext
bun add @rekkr/orm # Bun
npm install @rekkr/orm # Node.js 24.21+
npm install pg # Node.js + PostgreSQLSee Installation for the driver each database needs and what differs between the runtimes.
Extend Model directly. Table names follow conventions (User β users),
while fillable controls which attributes may be mass-assigned:
import { Model } from "@rekkr/orm";
class User extends Model {
static override fillable = ["name", "email"];
static override softDeletes = true;
posts() {
return this.hasMany(Post);
}
}
class Post extends Model {
static override fillable = ["user_id", "title"];
author() {
return this.belongsTo(User);
}
}Create the users and posts tables with default timestamp names and a
foreign key between them:
// database/migrations/<timestamp>_create_blog_tables.ts
import { Migration, Schema } from "@rekkr/orm";
export default class CreateBlogTables extends Migration {
async up() {
await Schema.create("users", (table) => {
table.id();
table.string("name");
table.string("email").unique();
table.string("role").default("member");
table.string("locale", 10).default("en");
table.timestamp("email_verified_at").nullable();
table.timestamps();
table.softDeletes();
});
await Schema.create("posts", (table) => {
table.id();
table.foreignId("user_id").constrained("users").cascadeOnDelete();
table.string("title");
table.text("body").nullable();
table.boolean("published").default(false);
table.timestamp("published_at").nullable();
table.timestamps();
table.index(["user_id", "created_at"]);
});
}
async down() {
await Schema.dropIfExists("posts");
await Schema.dropIfExists("users");
}
}See Models: timestamps for custom column names, including a camelCase model and matching schema.
Native getters can be serialized with appends without duplicating them in
static accessors:
class User extends Model {
static override fillable = ["firstName", "lastName"];
static override appends = ["fullName"];
get fullName(): string {
return `${this.getAttribute("firstName")} ${this.getAttribute("lastName")}`.trim();
}
}Create the shared configuration used by both the CLI and your application:
// orm.config.ts
import type { OrmConfig } from "@rekkr/orm";
const config: OrmConfig = {
connection: { url: "sqlite://app.db" },
};
export default config;Configure ORM once at application startup, then query your models:
import { configureOrm } from "@rekkr/orm";
import config from "./orm.config";
configureOrm(config);
const user = await User.where("email", "alice@example.com").firstOrFail();
const posts = await user.posts().get();
console.log(user.getAttribute("name"), posts.length);
// Three ways to list users:
const users = await User.orderBy("id").get(); // Collection of User models
const usersJson = await User.orderBy("id").json(); // JSON-ready objects
const usersRawJson = await User.orderBy("id").rawJson(); // JSON-ready objects without hydrating modelsOr use the DB facade for ad-hoc table access without a model:
import { DB } from "@rekkr/orm";
const rows = await DB.table("audit_logs")
.where("event", "login")
.orderBy("created_at", "desc")
.limit(10)
.get();See the Quickstart guide for the full walkthrough.
Use the canonical migration commands for generation, dry runs, and seeded rebuilds:
orm make:migration create_blog_tables
orm migrate --pretend
orm migrate:rollback --step=2 --pretend
orm migrate:refresh --seed
orm migrate:fresh --seed
orm migrate:fresh --seed --seeder=UserSeeder--landlord, --tenants, and --tenant apply to both phases. In production,
mutating migration commands require confirmation or --force; status and
pretend runs never prompt, and pretend does not execute SQL. Migration locking
is automatic, so there is no --isolated flag. With --json, stdout remains
one document.
| Topic | Summary |
|---|---|
| Installation | Add the package to a Bun or Node.js project. |
| Configuration | Connection, tenancy, migrations, seeders, type generation. |
| Quickstart | End-to-end walkthrough: install β config β migration β model β query. |
| Topic | Summary |
|---|---|
| Schema Builder | Tables, columns, indexes, foreign keys. |
| Migrations | Versioned schema changes, rollback, multi-tenant scopes, auto-create database / schema. |
| Seeders | Populate development and test data. |
| Transactions | DB.transaction(), explicit connection transactions, and nested savepoints. |
| Topic | Summary |
|---|---|
| Query Builder | Chainable where / join / with / aggregates, DB facade, raw queries. |
| Cache | Redis-backed cache API, query remember(), exact tag invalidation. |
| Search | PostgreSQL full-text search and SQLite FTS5. |
| Collections | map, filter, groupBy, and other helpers returned by get(). |
| Models | Defining models, casts, accessors, soft deletes, persistence. |
| Relationships | hasMany, belongsTo, belongsToMany, polymorphic, eager loading. |
| Validation | Typed Laravel-style validator with fluent rules and tenant-aware database checks. |
| Topic | Summary |
|---|---|
| TypeScript | Plain model declarations, generated attributes, typed builders, scopes, and accessors. |
| Type Generation | Generate attribute interfaces from your database schema. |
| Topic | Summary |
|---|---|
| Queue Jobs | Dispatch jobs to named queues, run workers with orm queue, retries, delays, failed-job tracking, database and Redis drivers. |
| Commands | Artisan-style CLI commands with signature DSL, argument/option parsing, output helpers, and orm run. |
| Topic | Summary |
|---|---|
| SvelteKit Helper | Typed route model binding and action validation helpers for +page.server.ts. |
| Policies | Register model/resource policies, use can / authorize, and enforce access in RouteBuilder. |
| Observers | Lifecycle hooks for creating, updating, deleting, and more. |
| Events | Application-level events with function listeners, class handlers, and temporary subscriptions. |
| Library Usage | Programmatic API via configureOrm(). |
| Testing | In-memory SQLite and transactional test isolation. |
The full index lives at docs/README.md.
MIT
This project is a fork of Bunny.
Performance and verification: benchmark history.