Skip to content

feat: Auto-generate OpenGraph images for dynamic content sharing #102

Description

@TheMaestr-o

header

Overview

When users share links to posts, songs, or books on WhatsApp, Telegram, or Twitter, the preview displays a generic placeholder. This results in lower engagement and inconsistent brand presentation across platforms.

Goal: Generate branded preview images automatically for each shareable resource.

Problem

Platform Current Behavior Desired Behavior
WhatsApp Empty/generic thumbnail Branded image with content title
Telegram Missing preview Cover image + headline
Twitter Blank card Formatted social card

Solution

Hybrid rendering approach: Canvas-based generation with KV caching for optimal performance.

Request Flow:

GET /api/v1/og?type=post&id=42&locale=de
  ├─ Check KV Cache
  │  ├─ Hit: Return cached PNG (<10ms)
  │  └─ Miss: Render new PNG (300-500ms) → Cache → Return
  └─ Response: image/png (1200×630px)

Implementation

New Files

File Purpose
apps/api/src/services/og-generator.ts Canvas rendering logic for image generation
apps/api/src/routes/og.ts API endpoint with KV caching

Modified Files

File Change
apps/api/src/index.ts Mount og route
apps/web/app/[locale]/posts/[slug]/page.tsx Configure generateMetadata()
apps/web/app/[locale]/songbooks/[slug]/[id]/page.tsx Configure generateMetadata()
apps/treasures/app/[locale]/books/[id]/page.tsx Configure generateMetadata()

Dependencies

canvas - Server-side image rendering in Workers

API Endpoint

GET /api/v1/og?type={post|song|treasure}&id={id}&locale={de|en}

Returns: image/png (1200×630px)
Cache-Control: public, max-age=3600, immutable

Technical Details

Image Composition

  • Background: Cover image (left 50%) with gradient overlay
  • Text area: Title, excerpt, metadata (right 50%)
  • Footer: Logo, publication date, type indicator
  • Dimensions: 1200×630px (OpenGraph standard)
  • Format: PNG with compression

Caching Strategy

Layer Duration Invalidation
KV (Workers) 24 hours Manual on content edit
HTTP Cache-Control 1 hour Standard TTL expiry
Cloudflare Edge 1 hour Standard TTL expiry

Error Handling

  • Cover image missing: Render with solid background color
  • Data not found: Return 404
  • Render timeout: Serve fallback from cache if available
  • KV unavailable: Proceed with HTTP caching only

Acceptance Criteria

  • Canvas rendering works for posts, songs, and treasures
  • KV cache stores and retrieves images correctly
  • First request returns valid PNG within 500ms
  • Subsequent requests return within 10ms (from cache)
  • Cache invalidation works on content updates
  • Missing cover images handled gracefully (no errors)
  • Preview renders correctly in Facebook Sharing Debugger
  • Preview renders correctly in Twitter Card Validator
  • All content types tested and working end-to-end

Testing

Manual validation:

# Generate OG image
curl "http://localhost:8787/api/v1/og?type=post&id=1&locale=de" > test.png

# Validate in tools
https://developers.facebook.com/tools/debug/sharing/
https://cards-dev.twitter.com/validator

Performance Targets

Metric Target
First render latency <500ms
Cached request latency <10ms
Image file size <100KB
Cache hit ratio >95%

Related

  • Uses existing infrastructure: D1, R2, Workers KV
  • Reuses canvas rendering patterns from existing codebase
  • No new external services required
  • GDPR compliant: no personal data collection

footer

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions