Stabilize Class
Core Stabilize class for database connections and repository management
Constructor
new Stabilize( config: DBConfig, cacheConfig: CacheConfig = { enabled: false, ttl: 60 }, loggerConfig: LoggerConfig = {}, existingClient?: DBClient, events?: StabilizeEmitter)Parameters:
- config Database configuration (required)
- cacheConfig Cache configuration. Defaults to
{ enabled: false, ttl: 60 }— caching is off unless you turn it on.redisUrlselects the backend: with it entries go to Redis, without it they are held in this process. - loggerConfig Logger configuration. Defaults to
{}. - existingClient An already-open
DBClientto use instead of opening a new connection. When one is supplied, the cache is not created and noconnection:openevent is emitted for it. - events An emitter to use instead of one of its own. Supply it when you need to hear
connection:open: that event fires from the constructor, before a handler registered onorm.eventsafterwards could exist. Build the emitter, subscribe, then pass it.
import { Stabilize, DBType, LogLevel } from "stabilize-orm";
const orm = new Stabilize( { type: DBType.SQLite, connectionString: "./data/app.db", retryAttempts: 3, retryDelay: 1000, }, { enabled: false, ttl: 60, strategy: "cache-aside", }, { level: LogLevel.Info, filePath: "./logs/stabilize.log", });DBType
export enum DBType { Postgres = "postgres", MySQL = "mysql", SQLite = "sqlite", MSSQL = "mssql", MongoDB = "mongodb",}A string enum, not a numeric one, so the member value is the lowercase string on the right and it is what appears in DDL and in healthCheck() output.
All four SQL dialects are supported targets. The driver behind MSSQL is SQL Server, reached through the mssql package; it is the newest of the four and the one whose dialect support differs most from the others — no FOR UPDATE clause, no BEGIN/COMMIT text, a server-side transaction object, and INT IDENTITY(1,1) rather than auto-increment syntax. MongoDB is a fifth target, and not a dialect. It is a document store, reached through the optional mongodb package, and its transactions need a replica set or sharded cluster.
// PostgreSQLnew Stabilize({ type: DBType.Postgres, connectionString: process.env.DATABASE_URL! });
// MySQL / MariaDBnew Stabilize({ type: DBType.MySQL, connectionString: process.env.MYSQL_URL! });
// SQLitenew Stabilize({ type: DBType.SQLite, connectionString: "./data/app.db" });
// SQL Servernew Stabilize({ type: DBType.MSSQL, connectionString: process.env.MSSQL_URL! });getRepository()
getRepository<T>(model: new (...args: any[]) => T): Repository<T>Gets a repository for a model to perform CRUD operations. Memoised per model: repeated calls with the same class return the same Repository instance, so it is safe to call this on every request.
const userRepo = orm.getRepository(User);const user = await userRepo.findOne(1);transaction()
async transaction<T>(callback: (txClient: DBClient) => Promise<T>): Promise<T>Executes a callback within an atomic database transaction.
await orm.transaction(async (txClient) => { const user = await userRepo.create({ id: generateUUID(), name: "Alice" }); const post = await postRepo.create({ id: generateUUID(), title: "Post", authorId: user.id });});getCacheStats()
async getCacheStats(): Promise<CacheStats>Returns cache hit/miss statistics, and which store answered — so a cache that is doing nothing is distinguishable from one that is merely cold.
const stats = await orm.getCacheStats();console.log(`Hits: ${stats.hits}, Misses: ${stats.misses}, Keys: ${stats.keys}`);console.log(`Backend: ${stats.backend}`); // "redis" | "memory" | "disabled"backend reports the configuration, not the connection: "redis" means a client was built, and ioredis connects lazily, so a later connection failure shows up in healthCheck() rather than here.
healthCheck()
async healthCheck(): Promise<{ status: string; database: string; latencyMs: number; cacheStatus: string }>Checks database and cache connectivity with latency. On MongoDB the liveness check is ping rather than SELECT 1, and both are wrapped the same way so an unreachable server lands in the same shape.
const health = await orm.healthCheck();// { status: "healthy", database: "sqlite", latencyMs: 0.5, cacheStatus: "disabled" }cacheStatus names the backend rather than reducing it to connected-or-not, because an in-process cache has no connection to report:
"disabled"—enabledwas false."in-memory"— noredisUrl, so the cache is confined to this process."connected"/"connected (miss)"— a Redis client was built and the round trip did or did not find its probe key."unknown"— the check threw.
Properties
class Stabilize { // The underlying DBClient. Pass this to a repository method's trailing // `client` argument to run that call on a specific connection. public client: DBClient;
// Process-wide event emitter. See the Events page. public events: StabilizeEmitter;}Raw SQL
async rawQuery<T = any>(query: string, params?: any[]): Promise<T[]>async rawExec(query: string, params?: any[]): Promise<{ affectedRows: number }>Both default params to []. rawQuery returns the rows; rawExec returns the driver's affected-row count.
const results = await orm.rawQuery("SELECT * FROM users WHERE age > ?", [18]);const { affectedRows } = await orm.rawExec("UPDATE users SET active = 0 WHERE lastLogin < ?", [oneYearAgo]);Schema Methods
async migrate(config: DBConfig, migrations: Migration[]): Promise<void>async autoMigrate(models: any | any[]): Promise<void>async seed(seeds?: any[]): Promise<void>async reset(models: any | any[]): Promise<void>Thin wrappers over the migration and seeding functions. autoMigrate creates missing tables, columns and indexes; seed() with no argument runs the seeds registered with defineSeed(); reset() drops each model's table and history table before re-running autoMigrate. These are destructive — see the Migrations guide.
Pool Stats & Shutdown
async poolStats(): Promise<{ active: number; idle: number; total: number }>async close(): Promise<void>poolStats() reports the driver pool where the dialect exposes one (SQL Server) and falls back to { active: -1, idle: -1, total: -1 } when it cannot — check for a negative total before displaying it. close() emits connection:close, closes the database connection and disconnects the cache; call it on shutdown.
const poolStats = await orm.poolStats();await orm.close();