xtablo-source/backend/README.md
Arthur Belleville 88f3706cd5
docs(01-04): add backend/README.md quickstart (closes FOUND-05)
- Five-minute clone-to-page onboarding doc covering prereqs, bootstrap,
  two-terminal dev workflow, common commands, and troubleshooting.
- Documents docker compose fallback for contributors not on podman (D-11).
- Documents two-terminal workflow: just dev + just styles-watch (D-14).
- Every `just <recipe>` referenced is a real recipe in backend/justfile.
- Uses 'bootstrap-downloaded' wording (not 'vendored') for tailwindcss
  and htmx.min.js per Codex review concern #4.
- Clarifies HTMX runtime no-CDN policy; the unpkg URL in justfile is the
  single authoritative version pin (D-10 / Codex concern #5).
- Top 3 pitfalls from RESEARCH surfaced in Troubleshooting.
- 195 lines, no emoji, no marketing voice.
2026-05-14 19:31:19 +02:00

195 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Xtablo backend
Go + HTMX + Postgres. Phase 1: Walking Skeleton.
This README is the contract for FOUND-05: a developer with the prerequisites below
should be able to clone the repo, follow the Quickstart, and see the HTMX-driven
page within ~5 minutes.
## Prerequisites
Install these on your dev machine before starting:
- **Go** ≥ 1.22 (this project's `go.mod` declares 1.26)
- **just** — task runner (`brew install just` on macOS, `cargo install just`, or see
<https://github.com/casey/just>)
- **podman** with `podman compose` (preferred per D-11) **or** **docker** with
`docker compose`
- **curl**
- **git**
You do **not** need to install `goose`, `templ`, `sqlc`, `air`, the Tailwind CLI, or
`htmx.min.js``just bootstrap` installs the Go tools into `$GOBIN` and
bootstrap-downloads the Tailwind binary and HTMX script into local, gitignored
paths.
## Quickstart
Clone-to-running-page in ~5 minutes. Run from inside `backend/`.
```
cd backend
cp .env.example .env # adjust DATABASE_URL if Postgres is not on localhost:5432
just bootstrap # installs goose/templ/sqlc/air; bootstrap-downloads tailwindcss + htmx.min.js
just db-up # starts postgres via podman compose (see fallback below)
just migrate up # applies migrations from ./migrations
just dev # terminal 1: brings up db, runs generate, then air on :8080
# in a SECOND terminal:
just styles-watch # rebuilds static/tailwind.css on .templ / .go changes
# open http://localhost:8080
```
The page should render with a "Fetch server time" button. Clicking it swaps an
ISO-8601 timestamp into the page via HTMX. If the page shows "No time fetched
yet." and nothing happens on click, see Troubleshooting.
`bootstrap` is the slowest step (Go tool installs + two HTTP downloads). It only
needs to run once per clone.
## docker compose fallback
`compose.yaml` is portable across podman and docker — the service definition is
identical. If you don't have podman:
- Replace `podman compose` with `docker compose` mentally throughout this README.
- The `just db-up` / `just db-down` recipes call `podman compose` directly. Run
`docker compose up -d postgres` / `docker compose down` instead, and continue
with the rest of the Quickstart unchanged.
(Decision D-11.)
## Project layout
```
backend/
cmd/
web/main.go # HTTP server entry point
worker/main.go # background worker (skeleton — boot/log/shutdown only)
internal/
db/ # pgxpool wiring + sqlc-generated queries
web/ # chi router, handlers, middleware, design-system
ui/ # custom templ component library (Button, Card, Badge)
session/ # placeholder — Phase 2
tablos/ # placeholder — Phase 3
tasks/ # placeholder — Phase 4
files/ # placeholder — Phase 5
migrations/ # goose .sql migrations
templates/ # .templ files (layout, index, fragments)
static/
htmx.min.js # bootstrap-downloaded by `just bootstrap`; gitignored; no runtime CDN
tailwind.css # generated by the Tailwind standalone CLI
bin/ # gitignored — tailwindcss CLI binary, etc.
.air.toml # air live-reload config
.env.example # committed; copy to .env
compose.yaml # local Postgres
go.mod / go.sum
justfile # task runner recipes — the source of truth for commands
sqlc.yaml
tailwind.input.css
README.md
```
HTMX is served from `/static/htmx.min.js` at runtime — no CDN. The justfile's
bootstrap-time `unpkg.com` URL is the single authoritative version pin (D-10).
## Environment variables
`backend/.env` is gitignored; `backend/.env.example` is committed and lists the
three keys consumed by `cmd/web` (and `cmd/worker` for `DATABASE_URL`):
| Variable | Description | Default |
| -------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- |
| `DATABASE_URL` | Postgres DSN used by the web + worker binaries and by `just migrate` | `postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable` |
| `PORT` | HTTP port for `cmd/web` | `8080` |
| `ENV` | `development` enables slog's text handler; `production` switches to JSON | `development` |
## Common commands
Every command in this table is a recipe in `backend/justfile`.
| Recipe | What it does | When to use |
| ----------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------- |
| `just bootstrap` | Installs Go CLI tools (`goose`, `templ`, `sqlc`, `air`); bootstrap-downloads `bin/tailwindcss` and `static/htmx.min.js` | Once per clone; re-run after deleting `bin/` or `static/htmx.min.js` |
| `just db-up` | Starts the local Postgres container | Before `just migrate up` / `just dev` if not already running |
| `just db-down` | Stops the local Postgres container | When you're done for the day |
| `just migrate up` / `migrate down` / `migrate status` | Applies / reverts / inspects goose migrations against `DATABASE_URL` | After `just db-up`, or any time you change `migrations/` |
| `just generate` | One-shot: `templ generate`, `sqlc generate`, Tailwind compile to `static/tailwind.css` | After editing `.templ`, query SQL, or `tailwind.input.css` |
| `just styles-watch` | Tailwind standalone CLI in `--watch` mode | In a second terminal alongside `just dev` (D-14) |
| `just dev` | Brings up Postgres, runs `just generate`, then runs `air` for Go live-reload on `:8080` | Main dev loop, terminal 1 |
| `just test` | `templ generate` then `go test ./...` | Before committing |
| `just lint` | `go vet ./...` and `gofmt -l` check | Before committing |
| `just build` | Generates assets, then builds `bin/web` and `bin/worker` | Producing release binaries locally |
| `just clean` | Removes `bin/`, `tmp/`, `static/htmx.min.js`, `static/tailwind.css`, and `*_templ.go` files | Reset to a fresh-clone state without dropping the Postgres volume |
## Worker (skeleton — Phase 1 only)
`cmd/worker` in Phase 1 boots, logs `worker ready`, and idles waiting for a
signal. Real job runtime lands in Phase 6 (D-03). To run it manually:
```
DATABASE_URL=postgres://xtablo:xtablo@localhost:5432/xtablo?sslmode=disable \
go run ./cmd/worker
```
Ctrl-C to exit.
## Troubleshooting
The three issues most likely to trip you up on a fresh clone:
- **"Fresh clone fails to build with `undefined: templates.Index`"** — Templ
generates `*_templ.go` files from `.templ` sources, and those generated files
are not committed. Run `just generate` (or `just dev`, which calls it) before
invoking `go build` directly. (Pitfall 1.)
- **"First request to `/healthz` returns 503 right after `just db-up`"** — The
Postgres container needs ~510 seconds to become healthy after `podman compose
up -d` returns. Check `podman compose ps` (or `docker compose ps`) for the
`healthy` status, or just wait and retry. Subsequent calls succeed. The 503
during warm-up is correct behavior, not a bug. (Pitfall 2.)
- **"Tailwind classes used in `.templ` files don't appear in the compiled CSS"** —
Tailwind v4 only scans content paths declared via `@source` in
`tailwind.input.css`. Confirm the file contains `@source
"../templates/**/*.templ";` (and equivalent globs for `internal/web/**/*.go`).
Re-run `just styles-watch` so the watcher picks up the config change.
(Pitfall 3.)
If something else is wrong and you want a clean slate without dropping the
Postgres volume:
```
just clean # removes bin/, tmp/, static/htmx.min.js, static/tailwind.css, *_templ.go
just bootstrap # re-download tools and assets
just dev # back to a working state
```
Run `just db-down` first if you also want to drop the Postgres container.
## What Phase 1 ships (and doesn't)
**Ships:**
- Project scaffold (`go.mod`, justfile, `.air.toml`, `tailwind.input.css`,
`sqlc.yaml`, `compose.yaml`)
- Local Postgres via `compose.yaml` (`pg_isready` healthcheck)
- goose migration pipeline (`migrations/0001_init.sql` is a no-op bootstrap)
- chi router with `/`, `/healthz`, `/demo/time`, `/static/*`
- slog-based structured logging with RequestID middleware
- Graceful HTTP shutdown
- pgxpool wiring exercised by `/healthz`
- templ + HTMX demo (root page + `hx-get` round-trip to a templ fragment)
- Custom templ design-system package at `internal/web/ui/` (Button, Card, Badge)
- Live-reload dev loop (`just dev` + `just styles-watch`)
- `cmd/worker` skeleton (boot, log, idle, shutdown)
**Does not ship — deferred:**
- Authentication, sessions, users → Phase 2
- Tablos CRUD → Phase 3
- Tasks / kanban → Phase 4
- File uploads + R2/S3 → Phase 5
- Real worker jobs → Phase 6
- Production deploy, Dockerfile, `/readyz` → Phase 7