@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
Markdown
# ⚡ @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 API — and real-time events baked right in. Build, evolve, and react to your database like never before.