← Wszystkie aktualności
ARTYKUŁ
22 września 2026

Dlaczego zbudowaliśmy Osprey, nasz headless CMS

Osprey trzyma schematy treści w TypeScript, dostarcza opublikowaną treść w czasie builda i dla wszystkiego, co prywatne, zwraca 404, a nie błąd uprawnień.

Większość headless CMS-ów zmusza do wyboru: narzędzie z edytorem wizualnym, które traktuje schemat jak raz wygenerowany blob w bazie danych, albo własne rozwiązanie, w którym każde wczytanie strony na żywo odpytuje CMS. Osprey, nasz własny headless CMS, zbudowaliśmy, aby ten wybór był zbędny: schemat treści wersjonowany jak kod i witryna publikowana jako pliki statyczne. Poniżej opisujemy, co kod robi naprawdę, a nie tylko to, co mówi materiał marketingowy.

Rzędy wysokich regałów wypełnionych książkami we wnętrzu biblioteki
Fot.: Szeronine (CC BY 4.0), Wikimedia Commons.

Schematy to kod, nie blob w bazie danych

W Osprey kolekcja jest definiowana w TypeScript i znajduje się w tym samym repozytorium co reszta aplikacji. Oznacza to tę samą kontrolę wersji, ten sam code review i te same typy end-to-end, które zespół już stosuje do kodu aplikacji, rozszerzone na model treści. Interfejs administracyjny to udogodnienie do edycji wpisów; nigdy nie jest źródłem prawdy dla samego schematu.

Treść trafia do witryny w czasie builda, nie przy każdym żądaniu

Loader @osprey/astro pobiera opublikowane wpisy z publicznego endpointu REST Osprey podczas builda i przekazuje je do content collections Astro, bez potrzeby klucza API dla tego publicznego odczytu. Opublikowana witryna to czysty statyczny HTML: Osprey jest zależnością czasu builda, a nie runtime'u. Jeśli build uruchamia się, gdy CMS działa wolno lub jest niedostępny, loader zachowuje wcześniej zbuforowane wpisy zamiast przerywać build.

Domyślnie prywatne, niewidoczne po odmowie

Kolekcja jest udostępniana w publicznym API tylko wtedy, gdy jej własna reguła access.read wprost dopuszcza anonimowego wywołującego; wszystko inne pozostaje prywatne. Żądanie dotyczące prywatnej kolekcji lub niepublikowanego dokumentu zwraca 404 zamiast błędu uprawnień, więc API nigdy nie potwierdza, że prywatna kolekcja w ogóle istnieje.

Metadane SEO podróżują z każdym wpisem

Każdy wpis zwracany przez publiczne API niesie połączony obiekt SEO: własny tytuł, opis, tagi Open Graph i Twitter oraz canonical URL wpisu mają pierwszeństwo, a domyślne ustawienia całego workspace'u uzupełniają brakujące elementy. Ten sam payload zawiera rozstrzygniętą dyrektywę robots oraz linki hreflang do każdego opublikowanego tłumaczenia danej strony, dzięki czemu frontend może wyrenderować swoje metatagi bez odtwarzania tej logiki.

Jedno wdrożenie, wielu tenantów

Osprey jest wielotenantowy: jedno wdrożenie może obsługiwać więcej niż jedną witrynę, każdą ograniczoną do własnego tenanta. Na publicznych trasach treści to, z którego tenanta czytać, jest ustalane na podstawie nagłówka host samego żądania, a nie wartości podanej przez klienta, więc anonimowe żądanie nie może odczytać treści innego tenanta, wysyłając inny identyfikator. Każde zapytanie do bazy danych wykonywane przez API jest wewnętrznie ograniczone do tenanta, zamiast polegać na pamięci każdego handlera.

Mechanika RODO, nie tylko deklaracja

Osprey ma endpoint prawa do bycia zapomnianym, który redaguje dane osobowe — zgłoszenia z formularzy, aktorów w dzienniku audytu, konta użytkowników — dla podanego e-maila lub id, ograniczony do jednego tenanta. Osobny endpoint eksportu generuje pełny manifest treści tenanta na potrzeby wniosków o przenoszenie danych. Wbudowany menedżer zgód na cookies traktuje jako zawsze aktywne tylko cookies ściśle niezbędne, a każdą inną kategorię domyślnie ustawia na odmówioną, dopóki odwiedzający nie wyrazi zgody, przy czym „odrzuć wszystko” ma taką samą wagę wizualną jak „zaakceptuj wszystko”. Ponieważ Osprey jest self-hosted, zespół może też uruchomić go w całości na infrastrukturze europejskiej; dedykowane gwarancje rezydencji danych w UE są częścią planu enterprise produktu, a nie domyślną cechą każdej instalacji.

Headless z założenia

Osprey przechowuje treść i udostępnia ją jako JSON; nie narzuca, jak ma wyglądać witryna. Zespół może zbudować frontend w Astro, Next.js lub dowolnym generatorze stron statycznych, który potrafi wywołać API REST, i używać Osprey wyłącznie jako warstwy treści pod spodem. Aktualne plany, w tym wersje self-hosted i enterprise, są wymienione na stronie produktu Osprey.

Co zrobić

  • Zdefiniuj model treści jako kolekcje TypeScript i wersjonuj go razem z kodem aplikacji.
  • Ustaw jawną regułę access.read na każdej kolekcji, którą chcesz udostępnić przez publiczne API; pozostaw ją nieustawioną, aby kolekcja została prywatna.
  • Dodaj loader @osprey/astro albo wywołuj API REST bezpośrednio, aby treść była pobierana w czasie builda, a nie przy każdym żądaniu.
  • Ustawiaj pola SEO dla pojedynczego wpisu tylko tam, gdzie mają różnić się od ustawień domyślnych całej witryny, a resztę zostaw domyślnym wartościom.
  • Do wniosków o przenoszenie i usunięcie danych używaj wbudowanych endpointów eksportu i prawa do bycia zapomnianym, zamiast budować własne.
  • Jeśli hosting wyłącznie w UE ma znaczenie dla twojej zgodności, hostuj Osprey na infrastrukturze europejskiej albo zapytaj o gwarancje rezydencji w planie enterprise.

Źródła: strona produktu Osprey, getosprey.dev.