HomePlaygroundExpressionsDocsDriversBlogStatusRoadmapChangelog GitHub

๐Ÿช QAIL

The AST Protocol

One query language. Every layer.

QAIL lets you build queries as typed objects (AST), then sends protocol bytes to PostgreSQL. This removes app-side SQL string interpolation on the AST path and keeps RLS and validation centralized.

Plain English: your app builds a typed query object, not a hand-built SQL string. PostgreSQL still does normal parse/plan/execute on the server.

QAIL AST
Qail::get("users")
    .select_all()
    .filter("active", Eq, true)
POSTGRESQL
SELECT * FROM users WHERE active = true
QDRANT
{ "must": [{ "key": "active", "match": true }] }
cargo install qail
zig fetch --save ...qail-zig/v0.2.0.tar.gz
Rust Python Go PHP Java Node C/C++ Rust Python Go PHP Java Node C/C++

Native Rust โ€ข Universal C-API โ€ข FFI Bindings

Real Benchmark: N+1 Battle Test

Same query. Same database. Same 50-row result set with 3 JOINs.
100 iterations, release build, buffer cache warmed.
Snapshot run: February 2026.

QAIL AST
449ยตs
1 query
GQL + DataLoader
1.52ms
~3 queries
GQL Naive
18.2ms ยท 40ร— slower
~151 queries
REST Naive
22.5ms ยท 50ร— slower
~151 queries

QAIL executes one planned query on this path. Naive resolver/endpoint patterns can explode to 151 round-trips without join batching.

View all benchmarks โ†’

The QAIL Stack

Four layers. One AST flows through all of them.

๐Ÿงฌ

qail-core

THE AST

Parser, transpiler, type system. Queries are typed data structures, not hand-built SQL strings in app code. Schema validation at compile time.

get users fields id, email
  where active = true
  order by created_at desc
  limit 10
๐Ÿ”Œ

qail-pg / qdrant

NATIVE DRIVERS

Protocol-level PostgreSQL driver with typed values and built-in RLS context per connection.

let driver = PgDriver::connect_env().await?;
driver.set_rls_context(
    RlsContext::operator("op-456")
).await?;
driver.fetch_all(&cmd).await?
๐Ÿš€

qail-gateway

AUTO-REST + BINARY PROTOCOL

Zero-code API from your schema. Auto-REST with FK expansion, policy engine, WebSocket subscriptions, binary AST wire format.

GET /api/bookings
    ?status.eq=confirmed
    &sort=created_at:desc
    &expand=routes,payments
    &limit=20
โš™๏ธ

qail-workflow

STATE MACHINES

Declarative multi-step orchestration. Database queries, notifications, and events in typed state machines.

Workflow::new("booking")
    .step("validate", validate_fn)
    .step("charge", payment_fn)
    .step("confirm", confirm_fn)
    .on_error(rollback_fn)

Every Entry Point โ†’ One Pipeline

REST params, text queries, or binary AST all converge to the same AST. Policy injection and schema validation happen once. SQL text output is optional for tooling/debug views.

REST
GET /api/users?active=true
Text
get users where active = true
Binary
postcard::to_allocvec(&cmd)
โ†“
QAIL AST
Qail { action: Get, table: "users", cages: [Filter(active = true)] }
โ†“
POLICY ENGINE
+ injects AND operator_id = 'op-456'
โ†“
PROTOCOL ENCODER
Encode AST โ†’ Parse / Bind / Execute + typed bind values
โ†“
POSTGRESQL (with PG-native RLS)
1 query. 1 connection. Done.

Supported Databases

One AST. Two powerhouses. Cache built-in.

PostgreSQL

PostgreSQL

Relational โ€ข Transactions โ€ข ACID

Production
Qdrant

Qdrant

Vector โ€ข AI/ML โ€ข Semantic Search

Production

Native Cache

Moka โ€ข TTL โ€ข Zero Latency

Built-in

Language Drivers

Native packages for your language. All powered by one Rust core.

Zig

Zig

Pure Zig โ€ข TLS โ€ข Pool v0.2.0
Rust

Rust

cargo add qail-core Cargo
PHP

PHP

31K q/s โ€ข 2.6x Eloquent GitHub
Go

Go

126K q/s โ€ข 4.2x GORM GitHub
Python

Python

130K rows/s COPY GitHub
Node.js

Node.js

FFI โ€ข Native Addon GitHub

View all drivers โ†’

๐Ÿ›ก๏ธ The "Wait, Stop!" Feature

The only migration tool that reads your code before it touches your data.

Terminal
$ qail migrate up schema.qail --codebase ./src

๐Ÿ›‘ BLOCKED: Safety Check Failed

You are dropping column 'status', but it is still used in:

   ๐Ÿ“„ src/queries.ts:25  โ†’ get portfolio fields status
   ๐Ÿ“„ src/api.rs:102     โ†’ Qail::get("portfolio").filter("status"...)

Fix your code first, or use --force to proceed.
๐Ÿ”

Scans Before Running

Searches .rs, .ts, .js, .py for queries that reference changing columns

๐Ÿ“

Points to Line Numbers

Shows exactly where your code needs updating before migration

๐ŸŽฏ

You're the Boss

Use --force to override when you know better

The data layer you don't write.

One AST. Protocol bytes. Compile-time safety. Built-in RLS.