Skip to content

db-semantic-plannerThe intent-first query planner

Declare what you want. The planner decides how. Then shows you why.

dbsp
pnpm add @dbsp/core @dbsp/adapter-pgsql

See it in action ​

dbsp

How it works ​

1

Declare intent

Write what you need in TypeScript or NQL — tables, filters, relations, aggregations.

orm.select('posts').where(eq('published', true)).include('author')
→
2

Planner decides

The semantic planner selects relation filters by cardinality, selects includes by query shape and capabilities, extracts CTEs, optimizes.

include-strategy: json_agg (PostgreSQL default)
→
3

Inspect everything

Every decision is visible via dump() — SQL, parameters, plan reasoning. Debug before you execute.

SELECT posts.*, COALESCE((SELECT json_agg(...) FROM users ...), '[]') AS author_json FROM posts WHERE posts.published = $1

Why db-semantic-planner? ​

vs Prisma

No codegen step. Full SQL observability via dump(). The planner shows you why it chose a strategy, not just what SQL it generated. Native multi-tenant with withSchema().

typescript
// See every decision the planner makes
const dump = orm.select('posts')
  .where(eq('published', true))
  .include('author')
  .dump();

dump.plan.decisions
// → [{ type: 'include-strategy',
//      choice: 'json_agg',
//      context: { relation: 'author', ... },
//      reasoning: 'Relation posts.author (belongsTo, cardinality: one) - using json_agg selected by nested output', ... }]

vs Drizzle

Automatic include strategy selection — no manual JOINs for relations. The planner resolves explicit include joins, relation hints, and applicable defaults before query shape and dialect capabilities; flat output uses join or lateral, nested output defaults to json_agg when supported, and recursive includes use cte. Built-in pgvector and ParadeDB helpers.

typescript
// Relations just work — planner decides strategy
const users = await orm
  .select('users')
  .include('posts.comments.author')
  .dump();

// Nested 3-level include — zero manual JOINs
// Result: users[].posts[].comments[].author

vs Kysely

First-class relation handling with .include(). Schema-level type inference without manual interface definitions. Multi-tenant isolation built-in, not bolted on.

typescript
// Multi-tenant in one line
const tenantOrm = orm.withSchema('acme_corp');

const users = await tenantOrm
  .select('users')
  .where(eq('active', true))
  .dump();
// → SELECT * FROM "acme_corp"."users" WHERE ...

Adapter-agnostic core. PostgreSQL-native today. ​

The core planner, schema DSL, query builders, and NQL work independently of any database. Write once, swap adapters.

PostgreSQL
v1.0 — Stable
pgvectorParadeDBRLSLATERALCTEsWindow FnDISTINCT ONNQL MutationsMulti-tenantRange Types
SQLite
Planned
DuckDB
Planned
MySQL
Planned

Ready to build? ​

7,700+tests
7packages
v1.0stable
MITlicense

v1.0.1 highlights: Range operators (rangeOverlaps, rangeContains, rangeContainedBy), dump({ queryName, correlationId }) for end-to-end request tracking, relation-column distinctOn(), NQL tag mutations, and conditional upsert guards.

Released under the MIT License.