Skip to content

Examples/Content

Headless CMS and publication site (example)

A headless CMS that stores drafts, revisions and schedules in Postgres and publishes them on a timer. A Next.js site renders the pages, a CDN caches them, and an image CDN resizes uploaded media.

Scale: 30 editors; 2M page views/day; 95% CDN hit rate

123456789101112CLIENTCMS adminCLIENTReadersEDGECDNSERVICENext.js siteSERVICECMS APIDATABASEPostgresOBJECT STOREMedia storageEDGEImage CDNWORKERSchedulerEXTERNALHosted search

Select a component to see what it is responsible for and which state it owns.

  1. 1CMS admin → CMS API: Save draft
  2. 2CMS API → Postgres: Write revision
  3. 3CMS admin → Media storage: Upload image
  4. 4CMS API → Scheduler: Publish event
  5. 5Scheduler → Postgres: Claim due posts
  6. 6Scheduler → Next.js site: Revalidate paths
  7. 7Scheduler → Hosted search: Update index
  8. 8Readers → CDN: Read article
  9. 9CDN → Next.js site: Render on miss
  10. 10Next.js site → CMS API: Fetch published content
  11. 11Readers → Image CDN: Load image
  12. 12Image CDN → Media storage: Fetch original
  • Request / response
  • Bulk data
  • Asynchronous

Select a part to trace its flows.

  • Request / response
  • Bulk data
  • Asynchronous

Parts 10

Flows 12

  1. 1CMS admin → CMS APISave draftRequest / response
  2. 2CMS API → PostgresWrite revisionRequest / response
  3. 3CMS admin → Media storageUpload imageBulk data
  4. 4CMS API → SchedulerPublish eventAsynchronous
  5. 5Scheduler → PostgresClaim due postsRequest / response
  6. 6Scheduler → Next.js siteRevalidate pathsRequest / response
  7. 7Scheduler → Hosted searchUpdate indexRequest / response
  8. 8Readers → CDNRead articleRequest / response
  9. 9CDN → Next.js siteRender on missRequest / response
  10. 10Next.js site → CMS APIFetch published contentRequest / response
  11. 11Readers → Image CDNLoad imageRequest / response
  12. 12Image CDN → Media storageFetch originalBulk data

Invariants 3

  • One editor's save never silently overwrites another's

    Optimistic concurrency: UPDATE … WHERE id = $1 AND revision = $2; zero rows shows a conflict.

    Kept by

  • A scheduled post is published once

    Due rows claimed with FOR UPDATE SKIP LOCKED; publishing an already-published revision is a no-op.

    Kept by

  • Readers see a published change within a minute

    Revalidation is sent after commit and retried with backoff; pages also expire after 10 minutes.

    Kept by