Skip to content

Decide whether to back the read-only catalogue API with a real service #432

Description

@richardthe3rd

Context

The proto-first personal API references Festival (festivals/{f}) and Drink (festivals/{f}/drinks/{d}) parent resources, but until now nothing actually defined or served them — they existed only as resource_definition stubs in drink_entry.proto. Meanwhile the catalogue is served as static JSON feeds (data.cambeerfestival.app/{festivalId}/{category}.json), fetched directly by the app via BeerApiService.

So we had two parallel worlds: a structured AIP personal API and an ad-hoc static feed, joined only by ID convention.

What's been done (branch claude/festival-api-drinks-d1au01, PR #433)

Drafted the contract for a read-only catalogue API to partner the personal API, without committing to a server. Everything lives in one package, cambeerfestival.festival.v1alpha, exposing two services over the same resource hierarchy (mirroring the app's two destinations):

  • CatalogService ("The Festival") — ListFestivals/GetFestival, ListDrinks (filter / order_by / paginated)/GetDrink, ListProducers/GetProducer. All reads.
  • MyFestivalService ("My Festival") — the existing personal DrinkEntry + aggregate DrinkSummary RPCs.

Resources: Festival (festivals/{f}), Producer (festivals/{f}/producers/{p}), Drink (festivals/{f}/drinks/{d}). Drink carries a ProducerReference (producer resource name + display_name denormalised for cheap rendering), keeping brewery detail normalised on the Producer resource — matching the producer-keyed feed.

Details:

  • Catalogue resources are defined canonically here; the duplicate resource_definition stubs were removed from drink_entry.proto (same-package references resolve directly).
  • Every catalogue data field is OUTPUT_ONLY; Festival.data_base_uri still points at the existing static feeds.
  • buf lint, AIP api-linter, and OpenAPI generation all pass; proto tasks remain dev-only (mise.dev.toml), not in CI.

Decision still open

The contract exists; the implementation is deliberately deferred. The static feeds remain the right tool for read-mostly, bounded, CDN-friendly catalogue data, so a real catalogue service is only worth building if/when we need:

  • server-side search/filter across festivals, or
  • a single denormalised "grid in one round-trip" call (catalogue + DrinkSummary + caller's DrinkEntry), or
  • a partner-facing surface beyond the raw feeds.

Until then, the documented REST binding can be served by the existing static feeds (or a thin reshaping Worker).

Resolved

  • Namespace/naming: unified everything under one package cambeerfestival.festival.v1alpha with two services, CatalogService + MyFestivalService. Promotion is granular down to the individual RPC via the version segment (the version is the package; a stable version carries only the ready methods, alpha keeps a copy of the stable surface plus what's still in flux), so one package does not couple stability. No X.XService stutter. See proto/buf.yaml / proto/README.md.
  • Producer as a resource: the feed is producer-keyed, so Producer is now a first-class resource and Drink references it (ProducerReference) instead of embedding the full record. Supports brewery-directory browsing and avoids duplicating brewery metadata across drinks.

Follow-ups to consider

  • Note that drink/producer ids are not stable year-to-year — a catalogue API inherits the same feed IDs and does not fix cross-year matching.
  • Optional server-side faceting/counts (the app currently derives availableStyles/styleCountsMap etc. client-side) — only needed if the server should own those.
  • If/when implemented, move proto tasks from mise.dev.toml into CI.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions