ADR 0005: Media / cover image storage¶
- Status: Accepted
- Date: 2026-08-24
Context¶
Book metadata is fetched externally (ADR 0004). OpenLibrary provides a Covers API and Google Books provides imageLinks. The question was whether to store cover image bytes locally, reference external cover URLs, or do nothing.
Constraints:
- Workers memory is 128 MB per isolate; never buffer large images in memory.
- Storing nothing means relying on a third-party URL that can rot (link rot, hotlinking blocks, CORS).
- Cloudflare R2 is S3-compatible blob storage with no egress fees and a native Workers binding.
- Cover images are non-critical — the app is fully functional without them.
- Each edition already has a
urlfield pointing to the edition's page on a store or publisher site. Those pages display the cover image.
Options¶
- Store nothing — hotlink the external cover URL — zero storage cost; fragile (covers disappear when the source removes or blocks them).
- Mirror covers into R2 on add — fetch once at add-time, store in R2, serve via a Worker route or a public R2 bucket. Durable; costs R2 storage.
- Store covers as BLOBs in the database — rejected: TiDB free row storage is precious; BLOBs in the DB are an anti-pattern.
- Do nothing — no cover URL stored at all — the edition's
urlfield links to the store/publisher page, which shows the cover. The user sees the cover when they click through. Zero storage, zero image handling, zero external dependencies.
Decision¶
Adopt option 4: do nothing — no cover URL stored.
Cover images are not stored, not referenced, and not fetched. Each edition has a url field pointing to the edition's page on a store or publisher site. That page displays the cover. The user sees the cover by clicking through. TomeTrove does not render cover images in its own UI.
Consequences¶
- Positive: zero storage cost (no R2, no database BLOBs); zero infrastructure to maintain; zero image processing or proxying; zero external URL dependencies; zero Workers memory concerns; the data model is simpler (no
cover_urlfield). - Negative: the TomeTrove UI has no cover thumbnails — the book list is text-only; users must click through to a store to see a cover; if a store page removes the cover image, TomeTrove has no fallback.
- Neutral: if cover thumbnails in the UI become important later, this decision can be reversed by adding a
cover_urlfield (option 1) or an R2 mirror step (option 2) without changing the core data model — theexternal_identifierstable already stores the OpenLibrary/Google Books IDs needed to reconstruct cover URLs. See the data model reference.