Phase 5 — The Request Line: api/v1 read contract
23 JUL AT 10:53 PM

Phase 5 — The Request Line: api/v1 read contract

0 LOVES 1 VIEWS
Call in, get the song: the api/v1 tag surface the app consumes — the post-detail JSON carries every tag block's ordered items (one eager-load, no DTO), and a new GET /tags/{slug}/posts endpoint feeds tag browsing. Shipped 2026-06-11.

You call the station, you name the song, the song comes back over the air. Phase 5 is the request line: the complete api/v1 tag surface the Flutter app consumes. Two deliverables, both Go: the post-detail JSON now carries every tag block's ordered tag_items → tag → group (the API half of the read contract — the web half shipped in Phase 3), and a new GET /tags/{slug}/posts endpoint feeds tag browsing.

Promoted out of Track B the day the Flutter team chose full interactive parity — the endpoint crosses into the Go backend, so it landed here, in the all-Go phase; the Flutter screens landed in Phase 6.

One eager-load, no DTO

api/v1 serializes ent types directly — the json tags are already on the generated models — so exposing tag blocks in the post-detail JSON is literally one eager-load on the api/v1 GetPostDetail. No DTO, no handler change, no response-struct change. The block serializes as edges.tag_items[], each item carrying sort_order and edges.tag with id, name, slug, and edges.group.name — the group-only path, identical to the web.

Two contract points worth naming: tag.id is in the contract — Flutter's model requires it and dedups the CEWL aggregate by it; and hidden tag blocks ship their items, asserted rather than filtered, consistent with the existing collection blocks and the hidden-feeds-CEWL decision. The only staleness is the API cache's TTL — the same eventual consistency every live field already has.

Module independence, verified: api/v1 is self-contained — its PostsReadService is a namesake of the kontent one, not a shared import. The two eager-loads are deliberate duplicates; the phase touched only the api/v1 side.

The endpoint — a mirror that adds a 404

GET /tags/{slug}/posts mirrors the category endpoint's B.V.S.R. skeleton — but deliberately adds the 404 the category handler omits: CategoryPosts discards the lookup error and answers 200 with a null list for an unknown slug; the tag endpoint resolves the tag first (GetTagBySlug, site-scoped) and returns a real 404 for an unknown or foreign-site slug. The list itself is the same block-chain filter as the web browse page, ported to the api/v1 service, with a typed Tag field on PageData.

Proof: 9 tests consolidated in tag_posts_test.go — contract shape, ordering, hidden-block items, unknown slug 404, foreign-site 404, block-chain correctness, pagination — with the full api/v1 suite green (56 ok) and a live smoke against the dev site.

American Graffiti — Tags as a Content Block (master)
American Graffiti — Tags as a Content Block (master)
Jul 23, 2026 American Graffiti
← Back to American Graffiti