Schema
Tables as Go values: columns, references, indexes, constraints, and the capabilities that decide what the outside world can reach.
A schema is ordinary Go values, and it is the source of truth for all of these. Each one is independently optional — a project can take the query builder and nothing else.
Schema
Tables as Go values: columns, references, indexes, constraints, and the capabilities that decide what the outside world can reach.
Queries & domain logic
A query is a value, so predicates compose on a branch. Hooks are where the rules live — one registration constrains every read of a model.
REST API
List, read, create, patch and delete per exposed table, with filtering, sorting, search, pagination and an OpenAPI document built from the schema.
TypeScript SDK
where admits only filterable columns with the operators their type
accepts, select narrows the response type, and a hidden column has no
spelling at all.
Go CLI
A cobra command tree over the same vocabulary, so --help states what a
resource accepts without sending a request — the form the guarantee has to
take for a caller with no compile step.
Migrations
A schema edit diffed into DDL your own runner applies — and an existing database read back into a schema file, which is the same machinery pointed the other way.
Static query generators cannot express “this WHERE clause exists only when the user typed something in the search box.” The usual workaround is string concatenation, which is why the HTTP layer of a filter/sort/search page is mostly boilerplate.
PostgREST solves that by making the database the API, but there is then nowhere to put Go domain logic, and the whole schema sits one policy mistake away from being public.
sqlb takes the middle path. A query is a value, so predicates can be added conditionally:
q := sqlb.Query[Post]().Where(sqlb.F("status").Eq("published"))if search != "" { q = q.Where(sqlb.F("title").Contains(search))}posts, err := q.OrderBy(sqlb.F("created_at").Desc()).Limit(50).All(ctx, db)and the REST filter grammar compiles into that same predicate AST:
?status=eq.published&title=contains.postgres&sort=-created_at&per_page=50One compiler, one bind-parameter discipline, one set of hooks — two producers. That is the whole design, and One grammar, two producers is the long version.
Capabilities are opt-in
Filterable, Sortable, Searchable, Hidden. A column that does not
declare a capability cannot be reached through it — ever. The failure is a
400 naming what would have worked, not a leak.
Hooks are the domain seam
BeforeQuery receives the query itself, so one registration constrains every
read of a model — including the ones generated REST handlers issue. Tenant
scoping stops being something each call site remembers.
Nothing runs unasked
SQL() renders text and args without executing. Explain plans against the
live schema without running. Diff returns changes as values; your runner
applies them.
No dependencies to inherit
The engine depends on the standard library alone, and a check in CI enforces it. Only the REST adapter pulls in Huma, and only if you use it.
Six worked applications, each settling a different question — and each saying out loud what it deliberately is not.
sqlb is pre-1.0, has one author and no observed consumers. That is the honest starting position, and no amount of feature work substitutes for elapsed time under real traffic. See compatibility for what the current tag freezes and which surfaces are expected to move, and the adoption review for an outside read on what that means in practice.
What is proven, and re-checked on every run rather than asserted: CI applies
the generated DDL to a real Postgres 18, reads it back with introspect, and
requires the round trip to be a fixpoint; the query path runs through a real
PgBouncer in transaction pooling, because that is the deployed topology; and the
blog example is generated from its schema, so every behaviour test in it is also
a test of the generator’s output.
Postgres only. LISTEN/NOTIFY, jsonb aggregation and RETURNING are all
load-bearing; multi-dialect support would cost the best features.