Code

sqlc — queries from the schema

Three files as tabs: sqlc.yamlinternal/db/queries/users.sqlinternal/db/connect.go — the config that points sqlc at your schema, the query you write, and the pool the generated code runs on.

1. sqlc.yaml — the one line that makes the whole "no drift" promise real:

sql:
  - engine: "postgresql"
    schema: "db/migrations"          # ← reads YOUR goose migrations
    queries: "internal/db/queries"
    gen:
      go:
        out: "internal/db"
        sql_package: "pgx/v5"

schema: "db/migrations" — sqlc learns the shape of users from the same migration file goose runs. There is no second definition to keep in sync.

2. internal/db/queries/users.sql — you write SQL, annotated with a name and result shape:

-- name: CountUsers :one
SELECT count(*) FROM users;

-- name: CountUsers :one tells sqlc: generate a Go method called CountUsers that returns one row.

3. internal/db/connect.go — a pgx connection pool, verified with a Ping so a misconfigured URL fails loudly at startup, not on the first request.

Generate the Go. Now run:

sqlc generate

sqlc reads the schema + the query and writes internal/db/db.go, models.go, and users.sql.gogenerated code you don't edit (like Templ's _templ.go). You get, for free and fully typed:

// in the generated users.sql.go — you wrote the SQL, sqlc wrote this:
func (q *Queries) CountUsers(ctx context.Context) (int64, error) {
	row := q.db.QueryRow(ctx, countUsers)
	var count int64
	err := row.Scan(&count)
	return count, err
}

And a User struct in models.go whose fields mirror the columns exactly — id UUID, email TEXT, password_hash TEXT, created_at TIMESTAMPTZ — because sqlc read them from your migration. You never hand-wrote that struct, and it can never disagree with the table.

Never hand-edit the generated files. db.go, models.go, users.sql.go all carry a // Code generated by sqlc. DO NOT EDIT. header — and they mean it. Change the SQL, run sqlc generate again. The generated code is a function of your schema and queries; editing it by hand reintroduces exactly the drift sqlc exists to prevent.