nlqdb

Architecture

One engine, seen from above.

Five ways to ask, one request path, and the right engine for your data. Zoom into a card to see inside it; click a piece to see what it does and what it talks to.

Loading the map…

How a question travels.

  1. You ask in plain English — from the chat, an HTML element, the CLI, an agent over MCP, or the SDK. Same endpoint either way.
  2. The edge router picks up the request at the nearest Cloudflare location, and auth & quota decide who you are (anonymous is fine) and what you may spend.
  3. The plan cache looks up the question by (schema_hash, query_hash). On a hit — the majority of traffic — the LLM is never involved.
  4. On a miss, the NL→plan compiler asks the LLM for a typed plan, and the validator checks it against an allowlist before it can run. The model never writes raw SQL.
  5. The executor runs the plan on the engine your data lives in and streams back the answer, the rows, and a trace with per-step timings.

You ask — five surfaces, one call.

Every surface is a projection of the same engine — the web chat, the <nlq-data> element, the CLI, the MCP server, and the SDK all end up making the same POST /v1/ask call with a goal in plain English.

Chat web app
nlqdb.com/app — every reply comes back as a one-sentence answer, the raw data, and a trace you can open.
<nlq-data> element
One HTML tag on any site: a goal in plain English in, rendered data out. The whole client is ≤ 6 KB.
nlq CLI
A single static Go binary. `nlq "how many signups today"` — goal-first, starts in milliseconds.
MCP server
Claude, Cursor, Windsurf, and Zed talk to nlqdb as agent memory over MCP — typed rows the agent writes as it learns.
SDK / HTTP API
POST /v1/ask { goal } — the one endpoint every SDK wraps, from TypeScript to Swift, Ruby, and Rust.

The engine — one request path, no backend to write.

One edge-routed request path: authenticate, check the plan cache, and only on a miss ask the LLM for a typed plan — which is validated before anything touches your data. The LLM never emits raw SQL.

Edge router
Cloudflare Workers at the edge — under 50 ms from anywhere. Every surface lands here first.
Auth & quota
Better Auth + Workers KV. Anonymous works out of the box; rate limits fire before any model spends a token.
Plan cache
Plans are content-addressed by (schema_hash, query_hash) — no invalidation, ever. 60–80% of queries skip the LLM entirely.
NL→plan compiler
On a cache miss, the LLM router turns English into a typed query plan — never a raw SQL string.
Validator
An AST allowlist between the model and your data: reads stay reads, writes are previewed, and DDL only ever comes from our deterministic compiler.
Executor
Runs the validated plan on the right engine and streams rows back — with the trace that shows every step and its timing.

Your data — the right engine per workload.

Storage is a routing decision, not your problem. Postgres is the default home; ClickHouse serves analytics-shaped workloads; the adapter layer is engine-agnostic so more engines slot in behind the same executor.

Postgres
The default home for your data — Neon under the hood, one schema per tenant, row-level security on.
ClickHouse
Bring the warehouse you already run — ClickHouse or Tinybird — and question it in English.
More engines roadmap
The adapter is engine-agnostic: a workload analyser watches your query shapes and proposes the engine that fits (Redis, D1, …).

Ask it something yourself → Why it's built this way