Skip to main content

Repository DSL, sqlc, or SQL

Three supported paths

Query shapeTool
Single-table CRUD, predicates, order, paging, locksplatform/database/sql Repo and Query
Stable complex SQL known at build timesqlc with dxsql.Conn
Dynamic complex SQL using JSONB, PostGIS, CTEs, or windowsdxsql.SQL / SQLOne

Choose by query shape, not personal preference.

Repository DSL

page, err := widgets.Where(
dxsql.Eq("org_id", orgID),
dxsql.ILike("name", searchPattern),
).Order(
dxsql.Desc("created_at"),
dxsql.Desc("widget_id"),
).Paged(ctx, paging.NewRequest(pageNumber, pageSize))

The DSL parameterizes values and quotes known columns. Raw accepts a controlled SQL fragment plus values; it is not permission to pass client text.

sqlc

queries := gen.New(dxsql.Conn(ctx, db))
rows, err := queries.TopWidgetsByOrganization(
ctx,
gen.TopWidgetsByOrganizationParams{OrgID: orgID},
)

Conn returns the ambient transaction when ctx carries one. Use generated types directly; do not wrap sqlc in another generic registration layer.

Dynamic complex SQL

rows, err := dxsql.SQL[widgetRow](
ctx,
dxsql.Conn(ctx, db),
statement,
orgID,
limit,
)

Build dynamic identifiers only from an allowlist. Values always remain parameters.

Review evidence

A change should state why its selected path fits. “Uses a window function with a stable shape” justifies sqlc. “Optional filters change the WHERE predicates, but the row shape is stable” can justify the DSL. Keep SQL visible enough to review indexes and plans.

Exercise

Write one list query with the DSL, one top-N-per-group query with sqlc-style SQL, and one dynamic geospatial filter with dxsql.SQL. Explain why moving all three into one abstraction would make review harder.

Check yourself

  • How does sqlc participate in Manager.Do?
  • What operations are intentionally absent from Query?
  • Is dxsql.Raw safe with a client sort string?
  • What evidence belongs in a persistence PR?