@linked-db/linked-ql
Version:
A query client that extends standard SQL with new syntax sugars and enables auto-versioning capabilities on any database
426 lines (293 loc) • 16.8 kB
Markdown
<div align="center">
# LinkedQL
_A modern take on SQL and SQL databases_
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
[![License][license-src]][license-href]
</div>
<picture>
<source media="(max-width:799px)" srcset="https://github.com/linked-db/linked-ql/blob/master/resources/linked-ql-mobile2.png?raw=true">
<source media="(min-width:800px)" srcset="https://github.com/linked-db/linked-ql/blob/master/resources/linked-ql-main2.png?raw=true">
<img src="https://github.com/linked-db/linked-ql/blob/master/resources/linked-ql-main2.png?raw=true" alt="LinkedQL Banner" width="100%">
</picture>
<br><br>
<div align="center">
Try an advanced form of SQL right on your database.<br>
**LinkedQL** is a database client (`client.query()`) that solves the modern database capability problem in a single interface — and in under `80 KiB min | zip`.<br>
Relationships • JSON • Schema • Reactivity • Versioning • Offline → **SOLVED**
</div>
<br>
<div align="center">
| Entry Point | Idea | Capabilities | More |
|:--|:--|:--|:--|
| [Quick Start](#-quick-start) | [What is LinkedQL](#️-what-is-linkedql) | [Language Capabilities](#1--language-capabilities) | [Documentation](#-documentation) |
| [Clients & Dialects](#️-clients--dialects) | [Why LinkedQL](#-why-linkedql) | [Runtime Capabilities](#2--runtime-capabilities) | [Progress](#-progress-next) |
| | | [Offline Capabilities](#3--offline-capabilities) | |
</div>
<br><br>
## ⚡ Quick Start
```bash
npm i @linked-db/linked-ql
```
```js
import { PGClient } from '@linked-db/linked-ql/pg';
const client = new PGClient({
host: 'localhost',
port: 5432,
user: 'postgres',
password: 'password',
database: 'myapp'
});
await client.connect();
const result = await client.query(`SELECT 10 AS value`);
console.log(result.rows); // [{ value: 10 }]
await client.disconnect();
```
> [!NOTE]
> You’re viewing **@linked-db/linked-ql** — the newest iteration.
> For the prev 0.3.x branch, see [linked-db/linked-ql@0.3.*](https://github.com/linked-db/linked-ql/tree/0.30.13).
## 🗄️ Clients & Dialects
LinkedQL ships with clients for each major SQL dialect.<br>
For PostgreSQL, MySQL, and MariaDB, it adapts seamlessly to each database through their respective native connector.
| **Dialect** | **Package** | **Docs** |
| :------------------ | :----------------------------- | :----------------------------------------------------------------------------------------- |
| PostgreSQL | `@linked-db/linked-ql/pg` | [Read → PG Docs](https://linked-ql.netlify.app/docs/setup#postgresql) |
| MySQL | `@linked-db/linked-ql/mysql` | [Read → MySQL Docs](https://linked-ql.netlify.app/docs/setup#mysql) |
| MariaDB | `@linked-db/linked-ql/mariadb` | [Read → MariaDB Docs](https://linked-ql.netlify.app/docs/setup#mariadb) |
| FlashQL (In-Memory) | `@linked-db/linked-ql/flash` | [Read → FlashQL Docs](https://linked-ql.netlify.app/docs/setup#flashql) |
<br><br>
## 🏗️ What is LinkedQL
LinkedQL is a database client that solves the modern database capability problem in a single interface.
Same familiar API as a classic client (`client.query()`), but **advanced SQL over your database** — bringing relational queries, live queries, a schema versioning system, offline capabilities and more.
LinkedQL is more **a modern take on SQL and SQL databases** than just a client.
Need the full power of SQL locally? LinkedQL runs as an **embeddable, in-memory database** — codenamed **FlashQL**.
Use it as a lighter replacement for SQLite or PGLite, with all of LinkedQL’s power built in.
## 🧭 Why LinkedQL
SQL and SQL databases have a **capability problem.**
Modern applications built around them have to wade through layers of **external tooling** as a consequence.<br>
(For example, need relational queries and realtime data? → typical setup: ORM + GraphQL layers.)
Rather than extend that layer with yet another prosthetic arm for a missing limb in SQL, **LinkedQL extends SQL itself** to close the gaps at their level — **syntax gaps at the language layer**, **runtime problems at the runtime layer.**
All of that comes built-in with the classic client API — giving your database an **automatic upgrade** in both **language** and **runtime capabilities**.
<br><br>
## `1 |` Language Capabilities
LinkedQL lets you speak an advanced form of SQL right on your database.
With syntax shorthands and first-class support for relationships and JSON, you skip the imperative parts of SQL and get to writing more **intentful** SQL.
LinkedQL automatically compiles your query down to the SQL your database understands.
| **Feature** | **Summary** | **Docs** |
| :---------------- | :------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------- |
| **DeepRefs** | Follow relationships using simple arrow notation (`a ~> b ~> c`). | [Read → DeepRefs Docs](https://linked-ql.netlify.app/docs/capabilities/deeprefs) |
| **JSON Literals** | Model JSON shapes directly in SQL using JSON literals (`{}`, `[]`). | [Read → JSON Docs](https://linked-ql.netlify.app/docs/capabilities/json-literals) |
| **UPSERTS** | Perform insert-or-update operations with a literal `UPSERT` statement. | [Read → UPSERTS Docs](https://linked-ql.netlify.app/docs/capabilities/upsert) |
### Examples
<details open name="lang-capab"><summary><b>(a)</b> JSON Literals — Structured Projection</summary>
> SQL constructs return shaped JSON directly — no post-mapping layer needed.
```js
const result = await client.query(
`SELECT { id, name, email } AS user
FROM users
WHERE id = 1;`
);
console.log(result.rows[0]);
// → { user: { id: 1, name: 'Jane', email: 'jane@example.com' } }
```
</details>
<details name="lang-capab"><summary><b>(b)</b> DeepRefs — Inline Relationship Traversal</summary>
> Follow foreign keys directly inside a query — joins expressed as natural relationships.
```js
const posts = await client.query(
`SELECT title, author ~> { name, email }
FROM posts
WHERE published = true;`
);
console.log(posts.rows[0]);
// → { title: 'Syntax Shorthands', author: { name: 'John Doe', email: 'john@example.com' } }
```
</details>
<details name="lang-capab"><summary><b>(c)</b> UPSERT — Insert-or-Update in One Step</summary>
> LinkedQL exposes UPSERT as a literal statement — cleaner and portable across dialects.
```js
await client.query(
`UPSERT INTO users (id, name, email)
VALUES
(1, 'Jane', 'jane@example.com'),
(2, 'James', 'j2@example.com')`
);
```
</details>
<br><br>
## `2 |` Runtime Capabilities
LinkedQL enables **SQL-level reactivity** and **automatic schema versioning** right on your database — **no plugins, database extensions, or middleware** required.
(A built-in **Realtime Engine** and **Timeline Engine** quietly extend your database at execution time.)
Modern apps and modern workflows — solved.
| **Feature** | **Summary** | **Docs** |
| :------------------ | :-------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------- |
| **Realtime SQL** | Run live, self-updating queries right on your database. | [Read → RealtimeSQL Docs](https://linked-ql.netlify.app/docs/capabilities/realtime-sql) |
| **Timeline Engine** | Get automatic database versioning on every DDL operation; bind queries to specific schema versions. | *(Coming soon)* |
### Examples
<details open name="runtime-capab"><summary><b>(a)</b> Live Queries — Continuous Results</summary>
> Turn on reactivity for any query with `{ live: true }` — get a live view of your data.
```js
const result = await client.query(
`SELECT p.title, p.category, p.views, u.name
FROM posts AS p LEFT JOIN users AS u ON p.author = u.id
WHERE p.published = true ORDER BY p.created_at DESC`,
{ live: true }
);
setInterval(() => console.log(result.rows), 1000);
// → Automatically updates as post or author data changes
```
</details>
<details name="runtime-capab"><summary><b>(b)</b> Live Queries + Language Shorthands</summary>
> Combine runtime reactivity with language-level extensions — relational traversal, JSON shapes, and more.
```js
const result = await client.query(
`SELECT
{ title, category, views } AS post,
author ~> { name, email } AS author
FROM posts WHERE published = true ORDER BY created_at DESC`,
{ live: true }
);
setInterval(() => console.log(result.rows), 1000);
// → Automatically updates as post or author data changes
```
</details>
<details name="runtime-capab"><summary><b>(c)</b> Version Binding — Point-in-Time Queries</summary>
> Anchor a query to a specific schema version — guard against breaking changes with semantic version control.
```js
const result = await client.query(
`SELECT name, email
FROM users@2_3
WHERE active = true;`
);
console.log(result.rows);
// → Runs against schema version 2.3 — unaffected by later migrations
```
</details>
<br><br>
## `3 |` Offline Capabilities
LinkedQL can run anywhere your app runs.
Its built-in **FlashQL** runtime brings all of LinkedQL to the **client**, the **edge**, and **offline** environments — same SQL, same semantics.
It extends that with built-in support for **federation**, **materialization**, and **sync** between remote databases and local state.
| **Capability** | **Summary** | **Docs** |
| :------------------ | :----------------------------------------------------------- | :------------------------------------------------------------------------- |
| **Federation** | Query across remote and local databases as a single surface. | [Read → FlashQL Docs](https://linked-ql.netlify.app/docs/flashql) |
| **Materialization** | Materialize remote datasets locally for offline queries. | [Read → FlashQL Docs](https://linked-ql.netlify.app/docs/flashql) |
| **Sync** | Two-way synchronization between local and remote databases. | [Read → FlashQL Docs](https://linked-ql.netlify.app/docs/flashql) |
### Examples
<details open name="offline-capab"><summary><b>(a)</b> Local Database — Runs Anywhere</summary>
> The same SQL engine that runs on the server — fully on the client.
```js
import { FlashClient } from '@linked-db/linked-ql/flash';
const client = new FlashClient();
await client.query(`CREATE TABLE users (id SERIAL, name TEXT)`);
await client.query(`INSERT INTO users (name) VALUES ('Alice'), ('Bob')`);
const result = await client.query(`SELECT JSON_AGG(name) AS users FROM users`);
console.log(result.rows);
// → [{ users: ['Alice', 'Bob'] }]
```
</details>
<details name="offline-capab"><summary><b>(b)</b> Federation — Local + Remote in One Query</summary>
> Query remote and local tables together — one SQL surface, automatic remote joins.
```js
await client.federate({ store: ['orders'] }, remoteConfig);
const result = await client.query(
`SELECT
u.name,
COUNT(o.id) AS total_orders
FROM users AS u LEFT JOIN store.orders AS o ON o.user_id = u.id
GROUP BY u.id
ORDER BY total_orders DESC`
);
console.log(result.rows);
// → combines local `users` and remote `orders` data transparently
```
</details>
<details name="offline-capab"><summary><b>(c)</b> Sync — Continuous Offline Resilience</summary>
> Keep local and remote data automatically aligned — bidirectional, incremental, and resumable.
```js
await client.sync({ store: ['orders'] }, remoteConfig);
const result = await client.query(
`SELECT
u.name,
COUNT(o.id) AS total_orders
FROM users AS u LEFT JOIN store.orders AS o ON o.user_id = u.id
GROUP BY u.id ORDER BY total_orders DESC`
);
client.on('sync:status', s => console.log('Sync status:', s.state));
client.on('sync:change', e => console.log('Δ', e.table, e.type));
// → local tables stay mirrored with remote updates — even after reconnects
```
</details>
<br><br>
## 📚 Documentation
| Feature | Description | Wiki Page |
| :---------------- | :----------------------------------------------------------- | :--------------------------------------------------------------------------- |
| **DeepRefs** | Declarative relationship traversal across foreign keys. | [DeepRefs →](https://linked-ql.netlify.app/docs/capabilities/deeprefs) |
| **JSON Literals** | Inline JSON modeling syntax — objects, arrays, aggregations. | [JSON Literals →](https://linked-ql.netlify.app/docs/capabilities/json-literals) |
| **UPSERTS** | Simplified `INSERT + UPDATE` hybrid statement. | [UPSERTS →](https://linked-ql.netlify.app/docs/capabilities/upsert) |
| **RealtimeSQL** | Live queries powered by the Realtime Engine. | [RealtimeSQL →](https://linked-ql.netlify.app/docs/capabilities/realtime-sql) |
| **FlashQL** | In-memory SQL runtime for offline, edge, and hybrid apps. | [FlashQL →](https://linked-ql.netlify.app/docs/flashql) |
## ⏳ Progress (`@next`)
| Component | Status | Note |
| :----------------- | :-------- | :-------------------- |
| Parser & Compiler | 🟩 `100%` | Stable |
| Transform Engine | 🟩 `100%` | Stable |
| Drivers (PG/MySQL) | 🟩 `97%` | Complete; MySQL nearing parity |
| FlashQL Engine | 🟩 `99%` | Expanding |
| Realtime Engine | 🟩 `99%` | Expanding |
| Timeline Engine | 🟨 `20%` | Planned |
| Migration Wizard | ⬜ `10%` | Planned |
| IDE Tooling | ⬜ `5%` | Early hooks |
| Docs (vNext) | 🟩 `95%` | Expanding |
> <!--💡--> Status Legend:<br>
> 🟩 Complete | 🟨 In Progress | ⬜ Not Started
<br><br>
## 🤝 Contributing
LinkedQL is in active development — and contributions are welcome!
Here’s how you can jump in:
- **Issues** → Spot a bug or have a feature idea? Open an [issue](https://github.com/linked-db/linked-ql/issues).
- **Pull requests** → PRs are welcome for fixes, docs, or new ideas.
- **Discussions** → Not sure where your idea fits? Start a [discussion](https://github.com/linked-db/linked-ql/discussions).
### 🛠️ Local Setup
⤷ clone → install → test
```bash
git clone https://github.com/linked-db/linked-ql.git
cd linked-ql
git checkout next
npm install
npm test
```
### 📝 Tips
- Development happens on the `next` branch — be sure to switch to it as above after cloning.
- Consider creating your feature branch from `next` before making changes (e.g. `git checkout -b feature/my-idea`).
- Remember to `npm test` before submitting a PR.
- Check the [Progress](#-our-progress-on-this-iteration-of-linkedql) section above to see where help is most needed.
## 🔑 License
MIT — see [LICENSE](https://github.com/linked-db/linked-ql?tab=MIT-1-ov-file)
[npm-version-src]: https://img.shields.io/npm/v/@linked-db/linked-ql?style=flat&colorA=18181B&colorB=F0DB4F
[npm-version-href]: https://npmjs.com/package/@linked-db/linked-ql
[npm-downloads-src]: https://img.shields.io/npm/dm/@linked-db/linked-ql?style=flat&colorA=18181B&colorB=F0DB4F
[npm-downloads-href]: https://npmjs.com/package/@linked-db/linked-ql
[bundle-src]: https://img.shields.io/bundlephobia/minzip/@linked-db/linked-ql?style=flat&colorA=18181B&colorB=F0DB4F
[bundle-href]: https://bundlephobia.com/result?p=@linked-db/linked-ql
[license-src]: https://img.shields.io/github/license/linked-db/linked-ql.svg?style=flat&colorA=18181B&colorB=F0DB4F
[license-href]: https://github.com/linked-db/linked-ql/blob/master/LICENSE