New concepts: none. This is the floor — a database and an auto-generated REST API over it, and nothing else.
db — PostgreSQL 16.3apiserver — PostgREST 13, auto-generating a REST API
from the schemainit.sql — one table (candidates) with
seed data| Anti-pattern | Fixed in |
|---|---|
| PostgREST connects as the Postgres superuser | v-b3 |
Password inline in compose.yml |
v-b4 |
| Ephemeral storage — data dies on teardown | v-b1 |
| Implicit default network | v-b1 |
depends_on waits for container start, not DB
readiness |
v-b2 |
docker compose up -d
curl http://localhost:3000/candidates
Expect a JSON array of five candidates.
curl http://localhost:3000/
returns the OpenAPI root.curl http://localhost:3000/candidates returns 5 rows.curl -X POST http://localhost:3000/candidates -H "Content-Type: application/json" -d '{"name":"Ginny"}'
docker compose down
docker compose up -d
curl http://localhost:3000/candidates
Ginny is gone. Only the 5 init.sql rows
come back. Storage did not persist.docker compose down
Local-only dev defaults. The Basic tier keeps
clearly-labelled literals so the lesson runs with no setup:
devpassword (postgres superuser, inline in
compose.yml). They are teaching values: never reuse
them anywhere reachable from a network, and never copy them
into a real deployment. Every published port in this variant binds to
127.0.0.1 only. Real, random, per-install secrets start at
v-b8 (generated by install.sh) and the Intermediate tier
(v-i2…v-i5). A committed .env.secret is never acceptable;
only *.example files are tracked.
Download or view the raw variant source files:
| File | Link | Notes |
|---|---|---|
compose.yml | download | docker-compose manifest |
init.sql | download | SQL bootstrap / seed |
README.md | download | raw Markdown source |