How it works (the composition)

This is the most advanced "compose around the engine" example. PostgREST thinks it's talking to Postgres because the server (crates/server) does three things on its behalf:

  • Version probe — answers PostgREST's startup checks so it proceeds (introspect.rs).
  • Catalog reflection — reflects the engine's tables and foreign-key relationships into the binary catalog format PostgREST loads into its schema cache (reflect.rs).
  • Data-path rewriting — rewrites PostgREST's fixed query templates (GET/POST/PATCH/DELETE and FK-embedding reads) into engine-runnable SQL, assembling nested JSON itself with a nested-loop join (datapath.rs).

So the engine never sees LEFT JOIN LATERAL, row_to_json, or json_agg — the server handles those. The result: relationships reflect into PostgREST's schema cache and embedding (?select=col,rel(col), both many-to-one and one-to-many) works end-to-end.

Step 1 — start the engine server

cargo run -p twill-server -- --listen 127.0.0.1:5433 --db file://./srv.db

Step 2 — create schema with foreign keys

The engine tracks FK metadata (from inline REFERENCES / table-level FOREIGN KEY), which is what PostgREST needs to expose embedding. (It is metadata only — the engine does not enforce referential integrity in this phase.)

CREATE TABLE authors (
  id   INTEGER PRIMARY KEY,
  name TEXT NOT NULL
);
CREATE TABLE books (
  id        INTEGER PRIMARY KEY,
  title     TEXT NOT NULL,
  author_id INTEGER REFERENCES authors(id)
);

Step 3 — point PostgREST at the server

# postgrest.conf
db-uri = "postgresql://[email protected]:5433/main?sslmode=disable"
db-schemas = "public"
db-anon-role = "postgres"
postgrest postgrest.conf   # unmodified PostgREST 14.x

Breakdown: the db-uri is the same cleartext DSN every client uses. PostgREST connects, reflects the schema cache (tables + FKs), and starts serving.

Step 4 — use the REST API

# list
curl 'http://localhost:3000/books'

# filter + select columns
curl 'http://localhost:3000/books?title=eq.Dune&select=id,title'

# FK embedding — many-to-one (book → its author)
curl 'http://localhost:3000/books?select=title,authors(name)'

# FK embedding — one-to-many (author → their books)
curl 'http://localhost:3000/authors?select=name,books(title)'

# create
curl -X POST 'http://localhost:3000/books' \
  -H 'content-type: application/json' \
  -d '{"id":1,"title":"Dune","author_id":1}'

Breakdown: the embedding queries (select=…,rel(col)) are decomposed by the server into per-relation engine queries whose nested JSON it assembles — a nested-loop join in composition glue, invisible to the engine.

Scope

Built against captured PostgREST SQL

The rewrite targets PostgREST 14.x's actual query templates. Standard CRUD and FK embedding are supported; very advanced PostgREST features outside those captured templates may not be. This is composition glue living entirely in crates/server — the engine core is untouched. For the broader composition philosophy see Auth (better-auth) and Analytics (DuckDB).

Next

Twill DB documentation · Licensed under BUSL-1.1. · Author