Skip to content

Latest commit

 

History

History
128 lines (103 loc) · 7.88 KB

File metadata and controls

128 lines (103 loc) · 7.88 KB

Stingerloom ORM

A decorator-driven TypeScript ORM with a typed expression DSL and layered multi-tenancy, built for PostgreSQL, MySQL, and SQLite.

npm version tests license TypeScript strict

Documentation · Getting Started · API Reference · Examples


Why Stingerloom?

  • Decorator-first — Define entities, relations, hooks, and validation with TypeScript decorators. Column types are inferred automatically.
  • Typed QueryDSL via Proxy, no codegenqAlias(Entity, "u") gives you IDE autocomplete on every column. Chain .eq / .gt / .like / .in, aggregates (.count() / .sum() / .avg()), CAST, date components, window functions, CASE/WHEN, subqueries, and JSON-path navigation — all returning type-safe expressions that compose freely across where() / having() / select().
  • Unit of Work plugin — Identity Map, dirty checking, cascade, batch flush, lazy proxies, and pessimistic locking via em.extend(bufferPlugin()). Single-level cache skips round-trips for repeated PK lookups.
  • Multi-tenancy built in — Layered metadata system (inspired by Docker OverlayFS) with AsyncLocalStorage-based context isolation. Zero cross-tenant leakage by design.
  • Three databases, one API — MySQL (incl. MariaDB-specific optimizations), PostgreSQL, and SQLite share the same EntityManager interface. Switch drivers without rewriting queries.
  • Schema Diff migrations — Compare live database state against entity metadata and auto-generate migration code. Supports true / "safe" / "dry-run" synchronize modes.
  • NestJS-ready — First-party module with @InjectRepository, @InjectEntityManager, and multi-DB named connections.

Quick Start

npm install @stingerloom/orm reflect-metadata
npm install pg        # or mysql2, better-sqlite3
import {
  EntityManager, Entity, PrimaryGeneratedColumn, Column, qAlias,
} from "@stingerloom/orm";

@Entity()
class Post {
  @PrimaryGeneratedColumn() id!: number;
  @Column() title!: string;
  @Column({ type: "int" }) views!: number;
  @Column({ type: "datetime" }) publishedAt!: Date;
}

const em = new EntityManager();
await em.register({ type: "postgres", entities: [Post], synchronize: true, /* ... */ });

// CRUD
const post = await em.save(Post, { title: "Hello World", views: 0, publishedAt: new Date() });
const found = await em.findOne(Post, { where: { id: post.id } });

// Typed QueryDSL — IDE autocomplete on every column
const p = qAlias(Post, "p");

const trending = await em.createQueryBuilder(Post, "p")
  .select([
    p.title.as("title"),
    p.views.as("views"),
    p.publishedAt.year().as("yr"),
  ])
  .where(p.title.containsIgnoreCase("typescript"))
  .andWhere(p.views.gt(100))
  .orderBy(p.views.desc())
  .getRawMany();

See the Getting Started guide for full setup instructions.

Features

Category Highlights
Modeling @Entity, @Column, @ManyToOne, @OneToMany, @ManyToMany, @OneToOne, eager/lazy loading, inheritance mapping (STI / TPT / TPC), UUID columns with UUIDv7
Querying find, findOne, findWithCursor, findAndCount, SelectQueryBuilder with JOIN / GROUP BY / HAVING; qAlias() typed expression chain — string / numeric / math helpers, CAST, date arithmetic + components, window functions, CASE WHEN, subquery operators, JSON-path navigation, raw SQL escape hatches
Mutations save, update, delete, softDelete, restore, upsert, batchUpsert, streamBatch, batch operations
Transactions @Transactional decorator, manual BEGIN / COMMIT / ROLLBACK, savepoints, isolation levels, deadlock retry, NOWAIT / SKIP LOCKED
Unit of Work em.extend(bufferPlugin()) — Identity Map, dirty checking, cascade, batch flush, lazy proxies, pessimistic locking, @Version optimistic locking
Multi-tenancy Layered metadata (OverlayFS model), MetadataContext.run(), PostgreSQL schema isolation, TenantMigrationRunner
Migrations SchemaDiff auto-detection (column rename heuristic), MigrationGenerator, CLI runner (npx stingerloom migrate:run | rollback | status | generate)
Observability N+1 detection, slow query warnings, EXPLAIN analysis, EntitySubscriber events, query listeners
Validation @NotNull, @MinLength, @MaxLength, @Min, @Max, Zod / Valibot schemas via qb.selectSchema(schema)
Schema definition Decorators or decorator-free EntitySchema; Prisma schema import
Infrastructure Connection pooling, read replicas, retry with backoff, per-query timeout, graceful shutdown, SSL/TLS, AsyncIterable streaming (stream()), MariaDB native UUID + INSERT … RETURNING
NestJS StinglerloomOrmModule.forRoot / forFeature, @InjectRepository, @InjectEntityManager, multi-DB named connections

Database Support

MySQL / MariaDB PostgreSQL SQLite
CRUD
Transactions
Schema Sync
Migrations
ENUM ✓ (native + sync)
JSON path queries ✓ (jsonb)
Full-text search
Window functions
Schema Isolation
Read Replica
INSERT … RETURNING MariaDB 10.5+
Native UUID storage MariaDB 10.7+ — (TEXT)
SSL / TLS

Examples

Example projects are included in examples/:

Project Description
nestjs-cats CRUD, relations, soft delete, cursor pagination, EntitySubscriber
nestjs-blog ManyToMany, upsert, 59 e2e tests (Users / Posts / Tags / Categories)
nestjs-todo Minimal CRUD — uses the published npm package
nestjs-todo-sqlite Minimal CRUD on SQLite via better-sqlite3
nestjs-multitenant PostgreSQL schema-based tenant isolation with TenantMigrationRunner
prisma-import-demo Generate Stingerloom entities from an existing Prisma schema

Contributing

Contributions are welcome. Please open an issue first to discuss what you'd like to change.

License

MIT