Try an advanced form of SQL right on your database.
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.
Relationships • JSON • Schema • Reactivity • Versioning • Offline → SOLVED
| Entry Point | Idea | Capabilities | More |
|---|---|---|---|
| Quick Start | What is LinkedQL | Language Capabilities | Documentation |
| Clients & Dialects | Why LinkedQL | Runtime Capabilities | Progress |
| Offline Capabilities |
npm i @linked-db/linked-qlimport { 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.*.
LinkedQL ships with clients for each major SQL dialect.
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 |
| MySQL | @linked-db/linked-ql/mysql |
Read → MySQL Docs |
| MariaDB | @linked-db/linked-ql/mariadb |
Read → MariaDB Docs |
| FlashQL (In-Memory) | @linked-db/linked-ql/flash |
Read → FlashQL Docs |
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.
SQL and SQL databases have a capability problem.
Modern applications built around them have to wade through layers of external tooling as a consequence.
(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.
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 |
| JSON Literals | Model JSON shapes directly in SQL using JSON literals ({}, []). |
Read → JSON Docs |
| UPSERTS | Perform insert-or-update operations with a literal UPSERT statement. |
Read → UPSERTS Docs |
(a) JSON Literals — Structured Projection
SQL constructs return shaped JSON directly — no post-mapping layer needed.
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' } }(b) DeepRefs — Inline Relationship Traversal
Follow foreign keys directly inside a query — joins expressed as natural relationships.
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' } }(c) UPSERT — Insert-or-Update in One Step
LinkedQL exposes UPSERT as a literal statement — cleaner and portable across dialects.
await client.query(
`UPSERT INTO users (id, name, email)
VALUES
(1, 'Jane', 'jane@example.com'),
(2, 'James', 'j2@example.com')`
);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 |
| Timeline Engine | Get automatic database versioning on every DDL operation; bind queries to specific schema versions. | (Coming soon) |
(a) Live Queries — Continuous Results
Turn on reactivity for any query with
{ live: true }— get a live view of your data.
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(b) Live Queries + Language Shorthands
Combine runtime reactivity with language-level extensions — relational traversal, JSON shapes, and more.
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(c) Version Binding — Point-in-Time Queries
Anchor a query to a specific schema version — guard against breaking changes with semantic version control.
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 migrationsLinkedQL 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 |
| Materialization | Materialize remote datasets locally for offline queries. | Read → FlashQL Docs |
| Sync | Two-way synchronization between local and remote databases. | Read → FlashQL Docs |
(a) Local Database — Runs Anywhere
The same SQL engine that runs on the server — fully on the client.
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'] }](b) Federation — Local + Remote in One Query
Query remote and local tables together — one SQL surface, automatic remote joins.
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(c) Sync — Continuous Offline Resilience
Keep local and remote data automatically aligned — bidirectional, incremental, and resumable.
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| Feature | Description | Wiki Page |
|---|---|---|
| DeepRefs | Declarative relationship traversal across foreign keys. | DeepRefs → |
| JSON Literals | Inline JSON modeling syntax — objects, arrays, aggregations. | JSON Literals → |
| UPSERTS | Simplified INSERT + UPDATE hybrid statement. |
UPSERTS → |
| RealtimeSQL | Live queries powered by the Realtime Engine. | RealtimeSQL → |
| FlashQL | In-memory SQL runtime for offline, edge, and hybrid apps. | FlashQL → |
| 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:
🟩 Complete | 🟨 In Progress | ⬜ Not Started
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.
- Pull requests → PRs are welcome for fixes, docs, or new ideas.
- Discussions → Not sure where your idea fits? Start a discussion.
⤷ clone → install → test
git clone https://github.com/linked-db/linked-ql.git
cd linked-ql
git checkout next
npm install
npm test- Development happens on the
nextbranch — be sure to switch to it as above after cloning. - Consider creating your feature branch from
nextbefore making changes (e.g.git checkout -b feature/my-idea). - Remember to
npm testbefore submitting a PR. - Check the Progress section above to see where help is most needed.
MIT — see LICENSE