← Al het nieuws
ARTIKEL
22 september 2026

Waarom we Osprey bouwden, onze headless CMS

Osprey bewaart contentschema's in TypeScript, levert gepubliceerde content op build-tijd en geeft voor alles wat privé is een 404 terug, geen permissiefout.

De meeste headless CMS'en dwingen een keuze af: een tool met visuele editor die het schema behandelt als een eenmalig gegenereerde database-blob, of een zelfgebouwde opzet waarbij elke paginalading het CMS live aanroept. Osprey, onze eigen headless CMS, is gebouwd om die keuze overbodig te maken: een contentschema dat als code wordt geversioneerd en een site die als statische bestanden wordt uitgeleverd. Hieronder staat wat de code daadwerkelijk doet, niet alleen wat de marketing belooft.

Rijen hoge boekenkasten vol boeken in het interieur van een bibliotheek
Foto: Szeronine (CC BY 4.0), Wikimedia Commons.

Schema's zijn code, geen database-blob

In Osprey wordt een collectie gedefinieerd in TypeScript en leeft ze in dezelfde repository als de rest van de applicatie. Dat betekent dezelfde versiebeheer, dezelfde codereview en dezelfde end-to-end types die een team al toepast op zijn applicatiecode, nu ook op het contentmodel. De beheerinterface is een gemak om items te bewerken; ze is nooit de bron van waarheid voor het schema zelf.

Content wordt geleverd op build-tijd, niet bij elke aanvraag

De loader @osprey/astro haalt gepubliceerde items op van het publieke REST-endpoint van Osprey tijdens de build en geeft ze door aan de content collections van Astro, zonder dat voor die publieke leesactie een API-sleutel nodig is. De uitgeleverde site is pure statische HTML: Osprey is een build-tijdafhankelijkheid, geen runtime-afhankelijkheid. Draait een build terwijl de CMS traag of onbereikbaar is, dan behoudt de loader de al gecachete items in plaats van de build te laten mislukken.

Standaard privé, onzichtbaar bij weigering

Een collectie wordt alleen op de publieke API blootgesteld wanneer de eigen access.read-regel een anonieme aanroeper expliciet toelaat; al het andere blijft privé. Een aanvraag voor een private collectie, of voor een niet-gepubliceerd document, komt terug als een 404 in plaats van een permissiefout, zodat de API nooit bevestigt dat een private collectie bestaat.

SEO-metadata reist mee met elk item

Elk item dat de publieke API teruggeeft, draagt een samengevoegd SEO-object: de eigen titel, beschrijving, Open Graph- en Twitter-tags en canonieke URL van het item krijgen voorrang, en workspace-brede standaardwaarden vullen aan wat ontbreekt. Dezelfde payload bevat de opgeloste robots-richtlijn en hreflang-links naar elke gepubliceerde vertaling van die pagina, zodat een frontend zijn metatags kan renderen zonder die logica opnieuw te bouwen.

Eén deployment, meerdere tenants

Osprey is multi-tenant: één deployment kan meer dan één site bedienen, elk beperkt tot zijn eigen tenant. Op de publieke contentroutes wordt bepaald uit welke tenant gelezen wordt aan de hand van de host-header van de aanvraag zelf, niet van een door de client meegegeven waarde, zodat een anonieme aanvraag niet de content van een andere tenant kan lezen door een ander tenant-id mee te sturen. Elke databasequery die de API uitvoert, is intern beperkt tot de tenant, zonder dat elke handler dat zelf hoeft te onthouden.

AVG-mechaniek, geen loze belofte

Osprey bevat een recht-op-vergetelheid-endpoint dat persoonsgegevens — formulierinzendingen, actoren in het auditlog, gebruikersaccounts — voor een opgegeven e-mailadres of id afschermt, beperkt tot één tenant. Een apart export-endpoint genereert een volledig manifest van de content van een tenant voor verzoeken om dataportabiliteit. De ingebouwde cookiebanner behandelt alleen strikt noodzakelijke cookies als altijd actief en zet elke andere categorie standaard op geweigerd totdat een bezoeker toestemming geeft, waarbij "alles weigeren" visueel evenveel gewicht krijgt als "alles accepteren". Omdat Osprey self-hosted is, kan een team het ook volledig op Europese infrastructuur draaien; specifieke garanties voor EU-dataresidentie horen bij het enterprise-plan van het product, niet bij elke installatie standaard.

Headless met opzet

Osprey slaat content op en levert die als JSON; het heeft geen mening over hoe een site eruitziet. Een team kan de frontend bouwen in Astro, Next.js of elke generator van statische sites die een REST-API kan aanroepen, en Osprey puur als contentlaag daarachter gebruiken. De huidige plannen, inclusief de self-hosted en enterprise varianten, staan op de Osprey-productpagina.

Wat te doen

  • Definieer je contentmodel als TypeScript-collecties en versioneer het samen met je applicatiecode.
  • Stel een expliciete access.read-regel in op elke collectie die je via de publieke API wilt aanbieden; laat ze ongedefinieerd om de collectie privé te houden.
  • Voeg de loader @osprey/astro toe, of roep de REST-API rechtstreeks aan, zodat content op build-tijd wordt opgehaald in plaats van bij elke aanvraag.
  • Stel SEO-velden per item alleen in waar ze moeten afwijken van de site-brede standaardwaarden, en laat die standaarden de rest invullen.
  • Gebruik de ingebouwde export- en recht-op-vergetelheid-endpoints voor verzoeken om dataportabiliteit en verwijdering, in plaats van je eigen te bouwen.
  • Als hosting binnen alleen de EU belangrijk is voor je compliance, host Osprey dan op Europese infrastructuur of vraag naar de residentiegaranties van het enterprise-plan.

Bronnen: Osprey-productpagina, getosprey.dev.