Nobody has started this yet — be first.
Business impact
There's no way to discover what tags exist at all without fetching every bookmark and computing the set client-side -- which means no tag-autocomplete, no "browse by tag" cloud/list UI, and no way to show a user their own tag vocabulary, without shipping the entire bookmark list to compute it.
Problem
No endpoint or store method returns the distinct set of tags in use.
Current behavior
GET /tags returns 404 -- there is no such route registered at all.
Expected behavior
GET /tags returns every distinct tag currently used by at least one bookmark, along with how many bookmarks use it, sorted by count descending (most-used tags first) then alphabetically as a tiebreak -- computed by Postgres, not by fetching every bookmark row into Go and counting there.
Steps to reproduce
curl -s http://localhost:8081/tags
Why this matters
Tags live in a text[] column, not a join table -- getting "distinct tag → count" out of that requires unnest()-ing the array in SQL (SELECT tag, count() FROM bookmarks, unnest(tags) AS tag GROUP BY tag ORDER BY count() DESC, tag ASC) rather than a plain GROUP BY on a column, which is a genuinely different pattern from every other query in this codebase so far.
Suggested approach
Add a Store method (e.g. ListTags) that runs an aggregate query unnesting the tags array; wire a GET /tags route and handler that calls it and returns [{"tag": "...", "count": N}, ...].
Acceptance criteria
Verification
go test ./practicetickets/... -run TestTicket09 -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b feat/no-tag-list-endpointFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/no-tag-list-endpointSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inmigrations/0001_init.sqlcurl http://localhost:8081/healthz
# {"status":"ok"}
To run without Docker, point the DB_* env vars from .env.example at a reachable Postgres and run go run ./cmd/server -- it applies the same migrations on startup.
The project's own test suite (go test ./...) is an integration suite against a real Postgres; it looks for one via BM_TEST_DB_HOST/BM_TEST_DB_PORT/BM_TEST_DB_USER/BM_TEST_DB_PASSWORD/BM_TEST_DB_NAME (defaults point at localhost:15433, database bookmarks_test) and skips cleanly rather than failing if that database is unreachable:
docker run -d --name bm_test_pg -e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=bookmarks_test -p 15433:5432 postgres:16-alpine
Work the tickets in PRACTICE_TICKETS.md (TICKET-01 through TICKET-10, all in one file, ordered easy -> hard); each names one Go test under practicetickets/:
go test ./practicetickets/... -run TestTicket01 -v # a single ticket
./practice_tickets_run.sh # all 10, clean pass/fail summary table
Level 2
Implement a feature
Extend the system within its own patterns.