One source of truth — Postgres, goose, sqlc
The home page has nothing real to say — its status card is hardcoded. Time to connect a database. By the end of this lesson Home will show a live count of registered users, read from Postgres.
Three tools, three jobs:
- PostgreSQL — the database.
- goose — migrations. Each schema change is a numbered
.sqlfile (00001_users.sql). goose applies pending ones in order and remembers which have run. We embed them and run them on startup, so a fresh deploy migrates itself. - sqlc — queries. You write plain SQL; sqlc generates type-safe Go functions from it. No ORM, no query-builder DSL — just SQL, with the compiler checking the Go side.
The lovely part — one source of truth. Here is the fact worth stopping on:
sqlc reads its schema straight from the goose migration files. Look at
sqlc.yaml:schema: "db/migrations". sqlc doesn't have its own copy of the table definitions — it reads the same.sqlfiles goose applies to the real database. So the generated Go cannot drift from the actual schema.
If you've used a typical ORM, you know the usual arrangement: the migrations describe the database, and separately a set of model classes describe what the code thinks the database looks like — and those two quietly diverge. You add a column in a migration, forget to update the model, and nothing complains until runtime. That whole category of bug cannot exist here, because there is only one description of the schema. Change the migration → regenerate → the Go types change to match, at compile time. Rename a column and forget to update a query? sqlc generate fails. The drift is caught before the code even builds.
You'll prove this in the homework: add a column to the migration, regenerate, and watch the generated Go grow the field with zero manual edits.
This lesson builds the data layer:
- A
userstable (goose migration) + embedding the migrations. sqlc.yaml+ a query, generated into Go.- A pgx connection pool.
- Wire it into
mainandHome— the live count.
Prerequisite — a running Postgres. You need Postgres locally.
createdb signflowand you're set; the config default connects topostgres://postgres@localhost:5432/signflow.No admin rights / Windows?
scoop install postgresqlinstalls a user-level Postgres (no admin needed), then start it withpg_ctl -D "$env:USERPROFILE\scoop\apps\postgresql\current\data" start.