Skip to content

A schema-reconciling PostgreSQL mapper for Go

You own your structs.PostgreSQL owns your schema.The generator proves they agree.

Neither representation is generated from the other. You write the structs; migrations own the schema. The orm command introspects both, reports every place they disagree, and generates typed metadata only from a mapping it proved.

What it reads like
// The type parameters are the point: a Predicate[User] cannot
// reach a query over Post, and a NOT NULL column has no IsNull.
users, err := db.Users.Query().
    Where(Users.Email.ILike("%@example.com")).
    Where(Users.CreatedAt.Gte(cutoff)).
    With(Users.Orders.OrderBy(Orders.Placed.Desc()).Limit(5)).
    OrderBy(Users.CreatedAt.Desc()).
    Limit(50).
    All(ctx)

What the compiler catches

Types come from the catalog

A column's Go type is what PostgreSQL says it is. A text column has Like and an integer does not; a NOT NULL column has no IsNull. None of it is a convention you have to remember.

Nullability is a property of the query

A NOT NULL column read through an outer join can still be NULL, so it widens. A select list that ignores that is refused at build time, not discovered as a scan error on the row that happened not to match.

Relations are asked for, never guessed

There is no lazy loading, so a loop over a slice cannot become a query per row. Loading is breadth-first and batched: the statement count follows the shape of the tree you asked for, never the number of rows in it.

Migrations are planned and proved

Declarations are diffed against the schema the migrations describe. Destructive changes are gated, materialized views have a safe lifecycle, and the artifacts are portable across every supported major.

One statement, always

Composition, CTEs, derived tables and UNION ALL compile through one AST and one writer, with one global placeholder namespace. Nothing is assembled from strings and nothing is stitched together in Go.

PostgreSQL's own types survive

Ranges keep their bound model, intervals keep months and days apart, and uuid, jsonb, arrays and PostGIS geometry stay themselves rather than being reduced to something Go already had.

Proved, not asserted

Every frozen milestone carries a mutation campaign: the code is deliberately broken and the suite has to notice. The compatibility matrix refuses to run against fewer than the five PostgreSQL majors the project claims.

14–18
PostgreSQL majors
56
mutation classes caught
0
survivors

For agents: llms.txt · all docs · API manifest

The gophers here were drawn for this site, in the spirit of the Go gopher — the original character is Renée French's, CC BY 3.0.