@dbsp/nql
v1.9.4
Published
NQL (Natural Query Language) - A human and LLM-friendly query language for databases
Maintainers
Readme
@dbsp/nql
NQL (Natural Query Language) — a human- and LLM-friendly pipe-based query language that compiles to IntentAST for @dbsp/core.
Installation
pnpm add @dbsp/nqlQuick Start
// doctest: skip — exec-only operation; compile from @dbsp/nql is not in doctest preamble and orm.from(intent).all() requires a real PostgreSQL connection
import { createPgsqlCompileOnlyAdapter } from '@dbsp/adapter-pgsql';
import { compile } from '@dbsp/nql';
// Compile an NQL query to a public intent bundle
const compiled = compile(
"users | where active = true | select name, email | order name asc | limit 20",
db.model
);
if (!compiled.success || !compiled.ast?.query) {
throw new Error(compiled.errors.map((e) => e.message).join(', '));
}
// Pass the whole bundle to the adapter; bound params are explicit public IR nodes
const adapter = createPgsqlCompileOnlyAdapter();
const query = adapter.compile(compiled.ast, { model: db.model });Syntax overview
-- Basic selection with filter
users | where status = 'active'
-- Computed columns and ordering
orders | select id, total, tax | order total desc | limit 10
-- Relations (auto-resolved from schema refs)
posts | include author | where published = true
-- CTEs (WITH clause)
with recent AS (orders | where createdAt > '2024-01-01')
recent | select id, total
-- Aggregation
orders | group customerId | select customerId, sum(total) as revenueKey features
- Pipe syntax — Readable left-to-right data flow (
table | filter | select | order) - SQL-style literals — Single-quoted strings (
'value'), not double-quoted - Named parameters — Bind runtime values with
:namein expression positions - CTE support —
WITH name AS (subquery)for named subqueries - Mutation support — Insert, update, delete, upsert, and
insert/upsert ... from ...pipelines - Schema-aware — Validates column names and relation paths against
ModelIRat parse time - LLM-friendly — Concise syntax designed for AI-generated queries
- Chevrotain-based — Robust lexer + parser with structured error recovery
- Composable — Output
IntentASTis the same type used by the TypeScript fluent builders
Named parameters
Use :name placeholders for runtime values and pass a params map to the compiler:
// doctest: skip — illustrative direct compiler params example
import { createPgsqlCompileOnlyAdapter } from '@dbsp/adapter-pgsql';
import { compile } from '@dbsp/nql';
const compiled = compile(
'users | where id = :id and active = :active | limit :limit',
db.model,
undefined,
{ params: { id: 42, active: true, limit: 10 } },
);
if (!compiled.success || !compiled.ast?.query) {
throw new Error(compiled.errors.map((e) => e.message).join(', '));
}
const adapter = createPgsqlCompileOnlyAdapter();
const query = adapter.compile(compiled.ast, { model: db.model });Missing params fail compilation. null binds SQL NULL; undefined, NaN, and Infinity are rejected. The @dbsp/core orm.nql template tag builds on the same mechanism for ${value} interpolation. See Named Parameters and Template Binding for the full contract.
Tag mutations
The @dbsp/core orm.nql tag can compile and execute final mutation statements. Use .dump() for compile-only inspection; mutation dumps expose parameters instead of query dump params.
const mutationDump = orm.nql<unknown>`
insert into users set name = ${'Alice'}, email = ${'[email protected]'}
`.dump() as {
sql: string;
parameters: readonly unknown[];
};
console.log(mutationDump.sql);
console.log(mutationDump.parameters);Read-only | bind statements can feed a final insert ... from ... or upsert ... from ... mutation:
const pipelineDump = orm.nql<unknown>`posts
| where published = ${false}
| select id, title, authorId, published, createdAt
| bind draft_posts
insert into posts from draft_posts`
.dump() as {
sql: string;
parameters: readonly unknown[];
};
console.log(pipelineDump.sql);
console.log(pipelineDump.parameters);Tag mutation execution uses the normal mutation hooks. Multi-statement tags require every non-final statement to end with | bind <name>, and writable mutation bodies inside | bind are rejected by the tag executor.
Documentation
License
MIT
