⚡ @trap_stevo/liveql
Supercharge your database workflow with a visually clean, ultra-intuitive SQL layer.
Chain elegant queries, trigger instant real-time events, and manage schemas effortlessly — all without ORM overhead, blending raw SQL power with modern developer ergonomics to make database interactions fast, fluid, and future-proof.
🚀 Features
- 🖋 Elegant Query Chaining – Build clean, composable SQL queries with zero boilerplate
- 🔄 Full DML + DDL Support – Insert, update, delete, and alter schemas in one unified API
- 📡 Real-Time Database Events – Subscribe to changes with instant notifications
- 🛠 Schema Builder – Create, drop, and alter tables with expressive syntax
- 🔍 Advanced Filtering – Chain AND, OR, IN, LIKE, and more without syntax clutter
- 🌐 Multi-Dialect Ready – Works with PostgreSQL, MySQL, SQLite
- 📜 Migration Support – Run SQL files from a folder, track applied migrations
- ⚡ No ORM Bloat – Keep full control of raw SQL with modern ergonomic tooling
⚙️ System Requirements
Node.js | ≥ 19.x |
npm | ≥ 9.x (recommended) |
OS | Windows, macOS, Linux |
Database | PostgreSQL, MySQL, SQLite |
API Specifications
⚡ LiveQL API
The root class for all LiveQL operations.
Instantiated with an existing database client (PostgreSQL, MySQL, or SQLite).
const LiveQL = require("@trap_stevo/liveql");
const db = new LiveQL(client);
table | table(name: string) | ❌ | Returns a QueryBuilder instance for building and running queries on a specific table. |
schema | schema(name: string) | ❌ | Returns a SchemaBuilder instance for DDL operations on a table. |
inspector | inspector | ❌ | Exposes the SchemaInspector API for table/column/index metadata. |
migrator | migrator | ❌ | Exposes the MigrationManager API for running/updating migrations. |
realtime | realtime | ❌ | Exposes the RealtimeManager API for LISTEN/NOTIFY and polling-based change events. |
enableRealtime | enableRealtime(table: string, events?: ("insert" | "update" | "delete")[], options?: {...}) | ✅ | Enables real-time listeners for a table. |
disableRealtime | disableRealtime(table: string, events?: string[]) | ✅ | Disables listeners for a table. |
enableDDLEvents | enableDDLEvents() | ✅ | (Postgres) Enables DDL trigger and LISTEN for schema changes. |
disableDDLEvents | disableDDLEvents() | ✅ | Removes DDL trigger and listener. |
setupRealtime | setupRealtime({ tables?: string[], events?: string[], ddl?: boolean, pollOptions?: {...} }) | ✅ | Convenience method to enable multiple tables/events at once. |
Constructor Options
new LiveQL(client);
Example
const { Client } = require("pg");
const LiveQL = require("@trap_stevo/liveql");
const client = new Client({ });
await client.connect();
const db = new LiveQL(client);
await db.schema("users").create([
{ name: "id", type: "SERIAL", primaryKey: true },
{ name: "name", type: "TEXT", notNull: true }
]);
await db.table("users").insert({ name: "Alice" }).run();
const rows = await db.table("users").select("*").run();
console.log(rows);
🖋 Live Query
db.table(name)
select | select(...fields: string[]) | ❌ | Set columns ("*" if omitted). |
where | where(field: string, value: any) or where(field: string, op: string, value: any) | ❌ | Add a WHERE clause. Supports =, >, <, >=, <=, <> , etc. |
and | and(field: string, valueOrOp: any, valueIfOp?: any) | ❌ | Chain an AND condition (alias to where with AND). |
or | or(field: string, valueOrOp: any, valueIfOp?: any) | ❌ | Chain an OR condition. |
like | like(field: string, pattern: string) | ❌ | LIKE clause (dialect-appropriate params). |
in | in(field: string, values: any[]) | ❌ | IN (...) clause with bound params. |
orderBy | orderBy(field: string, dir?: "ASC" | "DESC") | ❌ | Sorting. |
limit | limit(n: number) | ❌ | Limit rows. |
insert | insert(data: object | object[]) | ❌ | Prepare an INSERT (single or bulk). |
update | update(data: object) | ❌ | Prepare an UPDATE (use with where ). |
delete | delete() | ❌ | Prepare a DELETE (use with where ). |
run | run() | ✅ | Execute the built SQL with bound params. Returns driver result (rows on PG). |
Notes
- Placeholders are dialect-aware:
$1, $2, ...
(PostgreSQL) vs ?
(MySQL/SQLite).
- Parameter numbering resets on each
.run()
.
Examples
// Select with AND/OR/IN/LIMIT
await db.table("users")
.select("id", "name")
.where("active", true)
.and("age", ">", 18)
.or("email", "LIKE", "%@example.com")
.in("role", ["admin", "editor"])
.orderBy("name", "ASC")
.limit(20)
.run();
// Update
await db.table("users")
.update({ name: "Alice" })
.where("id", 1)
.run();
// Delete
await db.table("users")
.delete()
.where("id", 5)
.run();
🛠 Schema Builder
db.schema(name)
create | create(columns: Array<{ name, type, primaryKey?, autoIncrement?, notNull?, unique?, default? }>, options?: { ifNotExists?: boolean }) | ✅ | Create table with provided columns. |
drop | drop(options?: { ifExists?: boolean }) | ✅ | Drop the table. |
rename | rename(newName: string) | ✅ | Rename table. |
addColumn | addColumn(column: string, type: string, options?: {...}) | ✅ | Add a column. |
dropColumn | dropColumn(column: string) | ✅ | Drop a column. |
alterColumn | alterColumn(column: string, type?: string, options?: { notNull?: boolean, default?: any }) | ✅ | Change type / nullability / default. |
addPrimaryKey | addPrimaryKey(columns: string | string[]) | ✅ | Add primary key constraint. |
addForeignKey | addForeignKey(column: string, refTable: string, refColumn: string, options?: { onDelete?, onUpdate? }) | ✅ | Add foreign key. |
addIndex | addIndex(indexName: string, columns: string | string[], options?: { unique?: boolean, ifNotExists?: boolean }) | ✅ | Create (unique) index. |
dropIndex | dropIndex(indexName: string) | ✅ | Drop index (adds CASCADE on PG). |
Column Type Hints
- Auto-increment: PG
GENERATED ALWAYS AS IDENTITY
, MySQL AUTO_INCREMENT
default
accepts raw SQL strings like "CURRENT_TIMESTAMP"
.
Example
await db.schema("products").create([
{ name: "id", type: "SERIAL", primaryKey: true },
{ name: "name", type: "TEXT", notNull: true },
{ name: "price", type: "NUMERIC", default: 0 }
], { ifNotExists: true });
await db.schema("products").addColumn("stock", "INT", { default: 0 });
🔍 Schema Inspector
db.inspector
listTables | listTables() | ✅ | List table names. |
getColumns | getColumns(table: string) | ✅ | Column metadata. |
getPrimaryKeys | getPrimaryKeys(table: string) | ✅ | Primary key columns. |
getIndexes | getIndexes(table: string) | ✅ | Index metadata. |
getForeignKeys | getForeignKeys(table: string) | ✅ | FK metadata. |
Supports PostgreSQL / MySQL / SQLite (using information_schema
or PRAGMA
).
📂 Live Migration
db.migrator
run | run({ up?: string[], down?: string[] }) | ✅ | Run SQL strings directly (in order). |
runFromFolder | runFromFolder(dir: string, direction?: "up" | "down") | ✅ | Load *.up.sql /*.down.sql from a folder, track in migrations , run/rollback in order. |
Example
await db.migrator.runFromFolder("./migrations", "up");
// ...
await db.migrator.runFromFolder("./migrations", "down");
📡 Live Time
db.realtime
onEvent | onEvent(table: string, eventType: "insert" | "update" | "delete", handler: (payload) => void) | ❌ | Listen to table events; internally uses event name ${table}__${eventType} . |
listen | listen(channel: string) | ✅ | (PG) LISTEN channel . |
unlisten | unlisten(channel: string) | ✅ | (PG) UNLISTEN channel . |
startPolling | startPolling(table, eventType, interval, fetchFn, { idColumn?, updatedAtColumn? }) | ✅ | Start polling fallback for non-PG. |
stopPolling | stopPolling(table, eventType) | ❌ | Stop polling. |
Example
db.realtime.onEvent("users", "insert", (row) => console.log("New user:", row));
db.enableRealtime("users", ["insert"]);
📦 Installation
npm install @trap_stevo/liveql
⚡ Quick Start (PostgreSQL)
const { Client } = require("pg");
const LiveQL = require("@trap_stevo/liveql");
(async () => {
const client = new Client({
host: "localhost",
user: "postgres",
password: "pass123",
database: "testdb",
port: 5432
});
await client.connect();
const db = new LiveQL(client);
// Fresh table
await db.schema("users").drop({ ifExists: true });
await db.schema("users").create([
{ name: "id", type: "SERIAL", primaryKey: true },
{ name: "name", type: "TEXT", notNull: true },
{ name: "email", type: "TEXT", unique: true },
{ name: "created_at", type: "TIMESTAMP", default: "CURRENT_TIMESTAMP" }
], { ifNotExists: true });
// Realtime listeners
db.realtime.onEvent("users", "insert", (row) => console.log("INSERT:", row));
db.realtime.onEvent("users", "update", (row) => console.log("UPDATE:", row));
db.realtime.onEvent("users", "delete", (row) => console.log("DELETE:", row));
db.enableRealtime("users", ["insert", "update", "delete"]);
// Insert
await db.table("users").insert({ name: "John", email: "john@example.com" }).run();
await db.table("users").insert({ name: "Jane", email: "jane@example.com" }).run();
// Update
await db.table("users").update({ name: "Johnathan" }).where("id", 1).run();
// Delete
await db.table("users").delete().where("id", 2).run();
// Select
const result = await db.table("users").select("id", "name", "email", "created_at").run();
console.log("Users:", result.rows);
})();
📜 License
See License in LICENSE.md
⚡ SQL Power. Live Updates. Zero Bloat.
LiveQL gives you the raw speed of SQL with the flexibility of a modern query API — and real-time events baked right in. Build, evolve, and react to your database like never before.