UNPKG

@trap_stevo/liveql

Version:

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 while blending raw SQL power with modern developer erg

288 lines (211 loc) 16.5 kB
# ⚡ @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 | Requirement | Version | |------------|------------------------| | **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). ```js const LiveQL = require("@trap_stevo/liveql"); const db = new LiveQL(client); ``` | Method | Signature | Async | Description | |---------------|---------------------------------------------------------------------------|:----:|---------------------------------------------------------------------------------------------------| | `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** ```ts new LiveQL(client); ``` **Example** ```js const { Client } = require("pg"); const LiveQL = require("@trap_stevo/liveql"); const client = new Client({ /* ... */ }); await client.connect(); const db = new LiveQL(client); // Create a table await db.schema("users").create([ { name: "id", type: "SERIAL", primaryKey: true }, { name: "name", type: "TEXT", notNull: true } ]); // Insert + select await db.table("users").insert({ name: "Alice" }).run(); const rows = await db.table("users").select("*").run(); console.log(rows); ``` --- ### 🖋 Live Query `db.table(name)` | Method | Signature | Async | Description | |------------|---------------------------------------------------------------------------|:----:|-----------------------------------------------------------------------------| | `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)` | Method | Signature | Async | Description | |-----------------|---------------------------------------------------------------------------------------------------------------|:----:|----------------------------------------------------| | `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` | Method | Signature | Async | Description | |------------------|------------------------------------|:----:|-----------------------| | `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` | Method | Signature | Async | Description | |-----------------|-------------------------------------------------------|:----:|-----------------------------------------------------------------------------| | `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` | Method | Signature | Async | Description | |---------------|---------------------------------------------------------------------------------------------------|:----:|--------------------------------------------------------------------------------------------------| | `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](./LICENSE.md) --- ## ⚡ SQL Power. Live Updates. Zero Bloat. **LiveQL** gives you the raw speed of SQL with the flexibility of a modern query APIand real-time events baked right in. Build, evolve, and react to your database like never before.