Preskoči na sadržaj
Astro Strapi Headless CMS Web arhitektura

Zašto je Astro + Strapi moj default stack za sajtove vođene sadržajem

Sanin Mulić 8 min čitanja

Na ovom stack-u isporučio sam dvanaest marketinških sajtova, uz to dva produktna lansiranja i portfolio koji upravo čitaš. Evo zašto se stalno vraćam Astro-u i Strapi-u, i gdje ne bih posegnuo za njima.

Svakih nekoliko mjeseci dobijem isto pitanje od malog tima ili solo osnivača: “Koji stack da koristimo za naš marketinški sajt?” Moj odgovor je zadnje dvije godine isti: Astro na frontendu, Strapi kao headless CMS. Ovaj tekst je duža verzija tog odgovora, uključujući i mjesta gdje se vide šavovi.

Oblik problema

“Sajt vođen sadržajem” obično znači početna stranica, nekoliko stranica funkcija, blog, kontakt forma, povremeni case study-iji ili stranice servisa. Većinu toga mora moći uređivati neko ko nije tehničke struke. Potpuna SPA interaktivnost ne treba ni na jednoj ruti, ali cjelina svejedno mora djelovati promišljeno i skupo.

I klasični WordPress i Next.js rade, ali oba loše sjedaju ovom obliku. WordPress te tjera da se boriš s adminom starim 20 godina da bi uradio išta moderno, a frontend konvencije nagrađuju template razmišljanje umjesto komponentnog. Next.js znači da isporučuješ React framework, sa svim JS-om i hidracijom koje on vuče, za nešto što je funkcionalno statički sajt.

Astro je framework koji je konačno rekao “statično po default-u, interaktivno samo gdje treba.” Strapi isti stav primjenjuje na modeliranje sadržaja: schema živi u kodu i prolazi code review kao i sve ostalo, umjesto da je sklapaš klikanjem po admin UI-u. Zajedno pokrivaju ovakve projekte bolje od svega što sam probao.

Zašto Astro

Brzina puni naslove, i jeste brz. Pravi adut je arhitektonska disciplina koju ti Astro ne da preskočiti: sam odlučuješ koje komponente trebaju JavaScript, a koje ne, i to svaki put.

U Astro-u .astro komponenta renderuje se u HTML pri build-u i isporučuje nula JS-a. Uključuješ interaktivnost importujući Svelte (ili React, Vue, Solid, šta god) komponentu i dodajući client:load ili client:visible direktivu. Ta jedna odluka, “treba li ovo hidrirati?”, donosi se svjesno za svako ostrvo (island). Rezultat su sajtovi koji djeluju brzo jer zaista jesu statički, s interaktivnošću samo tamo gdje je potrebna.

Astro ti se, uz to, ne miješa u posao. Tooling je mali, a cijeli mentalni model staje u glavu. Buildovi su brzi, i kad nešto pukne, greška obično pokaže stvarnu liniju. U poređenju s debug-ovanjem Next.js hydration mismatch-a, ovo djeluje kao odmor.

Za ovaj portfolio svaka sekcija je statička .astro komponenta osim kontakt forme (Svelte 5 s runes), theme toggle-a i marquee-ja. Ukupni budžet client JS-a je ispod 30KB gzipped. Pokušaj to postići s Next.js-om i uporedivim setom funkcija.

Zašto Strapi

Probao sam alternative. Sanity ima predivan editor, ali mu je modeliranje sadržaja nametljivo na načine koji mi smetaju. Contentful je odličan i tako se i naplaćuje. Payload CMS je obećavajuć, ali te čvrsto veže za svoj admin. Decap i Tina su u redu za sitne sajtove i frustrirajuće preko toga.

Strapi 5 pogađa upravo one neglamurozne stvari:

  • Schema je commitovan kod. apps/cms/src/api/<name>/content-types/<name>/schema.json živi u git-u. Code review na schema izmjenama. Reproducibilno preko mašina. Bez klikanja kroz admin da povezeš novo polje.
  • GraphQL iz kutije. @strapi/plugin-graphql izlaže sve što izlaže i REST API, s ugrađenim i18n-om. Pišeš fragmente jednom i koristiš ih kroz query-jeve.
  • i18n radi kako treba. EN + BS, jedan pored drugog, s pravim locale-aware query-jevima. Većina CMS-ova ovo pokušava dodati naknadno. Strapiju je to prioritet.
  • Self-host ili Strapi Cloud. Svoj vrtim na Strapi Cloud-u za produkciju (managed Postgres, automatski backup-i), SQLite lokalno za dev. Data model je identičan.

Jedna promjena koju dokumentacija ne ističe dovoljno: Strapi 5 izbacio je deprecated entityService API i prešao na documents API uz koji je pisanje seed skripti sasvim ugodno. Taj detalj postane jako bitan čim sadržaj reprodukuješ kroz više okruženja.

Kako se sve to povezuje

Evo obrasca koji koristim na svakom projektu sad:

// apps/web/src/api-strapi/queries/blog-posts.ts
export async function getAllBlogPosts(locale: Locale = DEFAULT_LOCALE): Promise<BlogPost[]> {
  const data = await strapiFetch<{ blogPosts: BlogPost[] }>(
    `query GetAllBlogPosts($locale: I18NLocaleCode!, $limit: Int!) {
      blogPosts(locale: $locale, sort: ["publishedAt:desc"], pagination: { limit: $limit }) {
        ...BlogPostFields
      }
    }
    ${BLOG_POST_BUNDLE}`,
    { locale, limit: FETCH_ALL_LIMIT },
  );
  return data.blogPosts;
}

Ona linija pagination: { limit: N } bitnija je nego što izgleda. Strapi 5-ov GraphQL plugin tiho ograničava neograničene query-jeve na 10 rezultata, pa ćeš prvi put kad to udariš u produkciji s “list all” query-jem izgubiti 90 minuta tražeći podatke koji fale. Sad pišem FETCH_ALL_LIMIT = 100 jednom na vrhu svakog queries fajla i ne razmišljam o tome ponovo.

Build flow je onda: Strapi Cloud servira podatke preko GraphQL-a, Astro fetch-a pri build-u, Cloudflare Workers Builds deploy-uje statički output. Kontakt forma je jedan Astro endpoint s prerender = false, služen istim Worker-om. End-to-end Time-to-First-Byte je oko 80ms globalno.

Gdje ne bih koristio ovaj stack

Posegao bih za nečim drugim kad:

  • Proizvod je prava aplikacija. Dashboard-i, CRM-ovi, bilo šta autentifikovano i interaktivno na svakoj stranici. Astro-ov static-first model bori se s tobom ovdje. Koristi Next.js, SvelteKit, ili Remix.
  • Tim već govori WordPress tečno i editorijalni workflow je usko grlo. Ne nameći novi CMS funkcionalnom timu.
  • Treba ti real-time saradnja u editoru (Notion-style multiplayer). Strapi je single-user. Htjet ćeš Sanity ili custom rješenje.

Šta bih sljedeći put gradio drugačije

Nekoliko stvari koje sam naučio teško i drugačije bih radio na sljedećem projektu:

  1. Postavi build-time SEO audit prvog dana. Dodao sam ga kasnije za ovaj portfolio (apps/web/src/plugins/seo-audit.ts). Da je od početka gate-ovao svaki PR, poštedio bi me nekoliko kasnijih gašenja SEO požara.
  2. Izaberi i18n locale listu prije dizajniranja komponenti. Komponente koje nisu računale na bosanski prikaz morao sam kasnije prerađivati. Mnogo je jeftinije o locale-ima misliti već od prve skice.
  3. Odluči deploy target prije build-a. Cloudflare Workers Builds, Vercel i Netlify svaki imaju male ali stvarne razlike u tome kako rukuju header-ima, redirect-ima i edge funkcijama. Kasni izbor znači refaktoring config-a kasnije.

Povrat koji se ukamaćuje

Tiha pobjeda ovog stack-a je to što se negdje oko trećeg mjeseca prestane boriti s tobom. Kad jednom postaviš schemu i GraphQL fragmente, dodavanje novog content type-a ili novog locale-a košta jako malo. Sajtovi koje sam prije dvije godine gradio na Astro + Strapi-u još su na istim major verzijama i još su brzi, a svaki mogu predati drugom developeru bez sedmice arheologije.

Poslije dvije godine još nisam zažalio ni na jednom projektu na ovom stack-u.