Kamaankamaan

CMS headless per SvelteKit: guida alla configurazione e integrazione API

Le funzioni di load di SvelteKit sono il posto giusto per chiamare un CMS headless. La load solo server tiene le API key fuori dal client, i params guidano il routing della lingua e ISR o l'export al build coprono la cac

Junaid Khalid
Junaid Khalid
30 maggio 2026 · 9 min read

SvelteKit include un modello di caricamento dati che si adatta al lavoro con un CMS headless meglio della maggior parte dei framework. La separazione tra +page.ts (universale) e +page.server.ts (solo server) vi indica dove mettere la API key. L'oggetto params gestisce il routing della lingua e dello slug senza middleware aggiuntivi. Il risultato e' una configurazione che richiede un'ora a uno sviluppatore che ha gia' usato il framework e produce un blog che carica velocemente, si posiziona in modo pulito e resta in un singolo file di configurazione.

Questa guida collega SvelteKit a una REST API di un CMS headless, dall'inizio alla fine. Il codice e' scritto contro la REST API di Kamaan come esempio concreto, ma il pattern si trasferisce a qualunque CMS basato su REST.

Punti chiave

  • Usate +page.server.ts per le fetch al CMS. La funzione di load lato server tiene la API key fuori dal bundle client, e i dati restituiti al client non includono mai la credenziale.
  • Instradate la lingua con params. Una cartella chiamata [lang] produce un valore params.lang che la funzione di load legge per recuperare la localizzazione corretta.
  • Definite entries() per permettere a SvelteKit di pre-renderizzare gli URL degli articoli. La funzione restituisce le combinazioni di slug e lingua da pre-renderizzare al build, e il build genera un file HTML per ogni articolo per ogni lingua.
  • La REST API di Kamaan fornisce gli articoli con un query parameter language e restituisce il corpo, i campi SEO e lo slug con prefisso locale gia' popolati. La fetch e' una sola chiamata per pagina.
  • Lo stesso articolo viene pubblicato su /en/blog/, /es/blog/, /de/blog/, /fr/blog/, /it/blog/ con hreflang corretto in automatico. SvelteKit legge da Kamaan, Kamaan gestisce la distribuzione multilingue.

Le funzioni di load di SvelteKit sono l'unica cosa che dovete capire. Tre varianti:

+page.ts viene eseguita sia lato server durante l'SSR sia lato client durante la navigazione. Tutto cio' che importate qui finisce nel bundle client. Non mettete qui le API key.

+page.server.ts viene eseguita solo lato server. La funzione viene eseguita durante l'SSR, restituisce JSON al client per l'idratazione e viene eseguita di nuovo come endpoint remoto quando l'utente naviga. API key, variabili d'ambiente solo server e chiamate al database vivono qui.

+layout.server.ts viene eseguita lato server per ogni pagina che eredita il layout. Usatela per il contesto a livello di sito che richiede credenziali server, come un oggetto globale "site config" recuperato dal CMS.

Per un blog collegato a un CMS headless, ogni fetch va in +page.server.ts. Non esiste uno scenario in cui dovreste chiamare la API del CMS da +page.ts.

SvelteKit + headless CMS architecture: Kamaan REST API feeds page server load function, which renders page svelte SSR alongside layout params for language routing

Struttura del progetto per un blog multilingue

Il layout delle cartelle che supporta /[lang]/blog/[slug]:

src/routes/
  [lang]/
    blog/
      +page.server.ts        # blog index
      +page.svelte
      [slug]/
        +page.server.ts      # single article
        +page.svelte
  +layout.server.ts          # site-wide data
  +layout.svelte

La cartella [lang] produce un valore params.lang a ogni load. La cartella [slug] fornisce params.slug. SvelteKit gestisce il routing senza alcun plugin.

Il +page.server.ts minimo per un singolo articolo

Questa e' l'intera funzione di load per /[lang]/blog/[slug]:

import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
import { KAMAAN_API, KAMAAN_KEY, KAMAAN_SITE_ID } from '$env/static/private';

export const load: PageServerLoad = async ({ params, fetch }) => {
  const { lang, slug } = params;
  const url = `${KAMAAN_API}/sites/${KAMAAN_SITE_ID}/articles/${slug}?language=${lang}`;
  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${KAMAAN_KEY}` }
  });
  if (!res.ok) throw error(res.status, 'Article not found');
  const article = await res.json();
  return { article };
};

Alcune cose da notare. Gli import delle env usano $env/static/private, che e' il controllo a tempo di compilazione di SvelteKit per garantire che la variabile sia disponibile solo lato server. La fetch e' la versione aumentata da SvelteKit che gestisce correttamente i cookie e il comportamento cross-request. L'helper error() lancia un errore tipizzato di SvelteKit che il template error.html di SvelteKit renderizzera'.

La struttura dei dati restituita dalla REST API di Kamaan include title, content, excerpt, meta_title, meta_description, og_image, featured_image_url, slug, language e un array alternates con gli URL di tutte le altre versioni linguistiche. Quell'array alternates e' cio' che usate per renderizzare i tag link hreflang nell'head.

Rendering dell'articolo in +page.svelte

<script lang="ts">
  export let data;
  const { article } = data;
</script>

<svelte:head>
  <title>{article.meta_title}</title>
  <meta name="description" content={article.meta_description} />
  <link rel="canonical" href={article.canonical_url} />
  {#each article.alternates as alt}
    <link rel="alternate" hreflang={alt.language} href={alt.url} />
  {/each}
  <meta property="og:image" content={article.og_image} />
</svelte:head>

<article>
  <h1>{article.title}</h1>
  {@html article.content_html}
</article>

Il loop hreflang e' l'intera implementazione SEO multilingue sul lato SvelteKit. Kamaan emette l'array alternates; SvelteKit renderizza i tag link. Non c'e' bisogno di calcolare o mantenere i riferimenti incrociati manualmente.

Prerendering per le prestazioni

Per un blog di marketing, ogni articolo dovrebbe essere pre-renderizzato al build. Due modifiche:

// +page.server.ts
export const prerender = true;

export const entries = async () => {
  const res = await fetch(`${KAMAAN_API}/sites/${KAMAAN_SITE_ID}/articles?per_page=1000`, {
    headers: { Authorization: `Bearer ${KAMAAN_KEY}` }
  });
  const { articles } = await res.json();
  return articles.flatMap(a =>
    a.languages.map(lang => ({ lang, slug: a.slug }))
  );
};

La funzione entries() indica a SvelteKit quali rotte dinamiche pre-renderizzare al build. SvelteKit scrive quindi un file HTML per ogni articolo per ogni lingua nell'output di build. Il build viene eseguito una volta quando il contenuto cambia e i file statici risultanti vengono serviti da una CDN.

Per contenuti che si aggiornano frequentemente, impostate prerender a false e usate il livello di caching della piattaforma (Vercel ISR, Cloudflare Workers cache o lo stale-while-revalidate della vostra CDN). La fetch restituisce il contenuto piu' recente a ogni cache miss.

La pagina indice del blog

La pagina indice elenca tutti gli articoli per una determinata lingua. Stesso pattern, endpoint diverso:

// /[lang]/blog/+page.server.ts
import type { PageServerLoad } from './$types';
import { KAMAAN_API, KAMAAN_KEY, KAMAAN_SITE_ID } from '$env/static/private';

export const load: PageServerLoad = async ({ params, fetch }) => {
  const res = await fetch(
    `${KAMAAN_API}/sites/${KAMAAN_SITE_ID}/articles?language=${params.lang}&per_page=50`,
    { headers: { Authorization: `Bearer ${KAMAAN_KEY}` } }
  );
  const { articles } = await res.json();
  return { articles };
};

Paginazione, filtro per categoria e ordinamento avvengono tutti come query parameter sullo stesso endpoint. La forma REST resta uniforme tra le pagine.

Perche' questo pattern funziona

Le funzioni di load server di SvelteKit rimuovono la maggior parte della superficie su cui i blog multilingue vanno in errore. La API key non raggiunge mai il bundle client. Le annotazioni hreflang vengono renderizzate dai dati server, non calcolate a runtime. Il build puo' produrre HTML statico per ogni articolo in ogni lingua, che e' cio' che il crawler di Google premia. Il bundle ridotto del framework e il rendering lato server mantengono i Core Web Vitals dove devono essere.

L'Auto-Multilingual Delivery di Kamaan gestisce la parte che SvelteKit non puo' coprire: produrre i corpi tradotti, gli slug e i campi SEO. Il CMS pubblica le quattro versioni tradotte; SvelteKit le legge con la stessa fetch di una riga. Un'unica configurazione, quattro lingue in produzione, nessun workflow separato.

Due flussi di lavoro reali

Un fondatore SaaS bootstrappato avvia un blog SvelteKit in un weekend. Collega un singolo +page.server.ts alla REST API di Kamaan, fa il deploy su Vercel e collega il proprio dominio. Il blog e' in produzione in inglese con la struttura URL /blog/[slug]. Quando attiva Auto-Multilingual Delivery, lo stesso codice SvelteKit serve /es/blog/[slug], /de/blog/[slug], /fr/blog/[slug], /it/blog/[slug] dal build successivo. Tempo totale di sviluppo: meno di due ore.

Una piccola agenzia gestisce tre siti SaaS clienti tutti su SvelteKit. Il sito di ogni cliente segue lo stesso pattern alimentato da Kamaan con un file env diverso. I nuovi articoli vengono pubblicati da un unico account Kamaan e ricompilano le tre app SvelteKit via webhook. L'operatore dell'agenzia non modifica mai il codice Svelte; il CMS fa il lavoro.

FAQ

Dovrei usare +page.ts o +page.server.ts per le fetch al CMS?

Sempre +page.server.ts. Le credenziali e l'URL della API appartengono al server. I dati restituiti al client dovrebbero essere gia' filtrati a quello che la pagina renderizza.

SvelteKit ha un adapter specifico per Kamaan?

No, e non ne ha bisogno. La REST API di Kamaan restituisce JSON standard. Qualunque fetch di SvelteKit funziona. Il pattern e' identico al fetch da qualunque endpoint REST.

Come gestisco i contenuti in preview dal CMS?

Aggiungete un query parameter alla chiamata API (?status=draft) e proteggetelo dietro un controllo su variabile d'ambiente. Il pattern di preview mode di SvelteKit usa i cookie per abilitare l'accesso ai draft solo sul deployment di preview.

Come pre-renderizzo migliaia di articoli in modo efficiente?

La funzione entries() restituisce tutte le combinazioni da pre-renderizzare. SvelteKit gestisce la concorrenza al build. Per blog molto grandi, passate a ISR o al rendering lato server con caching CDN per ammortizzare il costo del build.

Funziona con adapter-static per un export interamente statico?

Si'. prerender = true piu' entries() permette ad adapter-static di generare l'intero set di file HTML. Aggiungete una regola di fallback per le lingue aggiunte dopo il build e attivate la ricompilazione quando i contenuti cambiano.

E le rotte autenticate che hanno bisogno del CMS?

Usate un hook di SvelteKit (hooks.server.ts) per verificare la sessione prima che la load venga eseguita. La fetch al CMS resta in +page.server.ts, ma la load restituisce 401 se la sessione non e' valida.

Correlati su Kamaan

Inizia con Kamaan

Un endpoint REST, tutte le lingue in produzione

Kamaan vi offre un'unica dashboard per tutti i blog dei vostri prodotti, distribuita tramite una REST API pulita a qualunque framework. Tradotto in automatico in 99+ lingue a ogni pubblicazione. Un singolo account copre siti illimitati a 19$ al mese, fisso. Primo mese gratis.

Inizia gratis su kamaan.io

Frequently asked

FAQ · 6 ITEMS
Dovrei usare +page.ts o +page.server.ts per le fetch al CMS?

Sempre `+page.server.ts`. Le credenziali e l'URL della API appartengono al server. I dati restituiti al client dovrebbero essere gia' filtrati a quello che la pagina renderizza.

SvelteKit ha un adapter specifico per Kamaan?

No, e non ne ha bisogno. La REST API di Kamaan restituisce JSON standard. Qualunque fetch di SvelteKit funziona. Il pattern e' identico al fetch da qualunque endpoint REST.

Come gestisco i contenuti in preview dal CMS?

Aggiungete un query parameter alla chiamata API (`?status=draft`) e proteggetelo dietro un controllo su variabile d'ambiente. Il pattern di preview mode di SvelteKit usa i cookie per abilitare l'accesso ai draft solo sul deployment di preview.

Come pre-renderizzo migliaia di articoli in modo efficiente?

La funzione `entries()` restituisce tutte le combinazioni da pre-renderizzare. SvelteKit gestisce la concorrenza al build. Per blog molto grandi, passate a ISR o al rendering lato server con caching CDN per ammortizzare il costo del build.

Funziona con adapter-static per un export interamente statico?

Si'. `prerender = true` piu' `entries()` permette ad adapter-static di generare l'intero set di file HTML. Aggiungete una regola di fallback per le lingue aggiunte dopo il build e attivate la ricompilazione quando i contenuti cambiano.

E le rotte autenticate che hanno bisogno del CMS?

Usate un hook di SvelteKit (`hooks.server.ts`) per verificare la sessione prima che la load venga eseguita. La fetch al CMS resta in `+page.server.ts`, ma la load restituisce 401 se la sessione non e' valida.

Junaid Khalid
Written by
Junaid Khalid

Junaid Khalid is the founder of Kamaan, a headless blog CMS that auto-publishes in five languages and lets you manage every product blog from one dashboard.