Salta ai contenuti

Contratto dati

Tutto ciò che arriva da WordPress è unknown finché non passa da Zod. Nessun any, nessun accesso diretto a response.meta.campo senza validazione.

REST JSON → schema Zod → mapper → modello di dominio → componente
  • schema: descrive esattamente ciò che WordPress restituisce (meta compresi).
  • mapper: traduce i nomi (sort_date → sortDate) e risolve i media.
  • modello di dominio: ciò che serve alla UI, indipendente dal CMS.

src/cms/wordpress/wordpress-evento.schema.ts:

const WordPressEventoMetaSchema = z.object({
date: z.string().default(""),
sort_date: z.string().default(""),
location: z.string().default(""),
cat: z.string(),
// ...
});
export const WordPressEventiSchema = z.array(
WordPressPostSchema.extend({ meta: WordPressEventoMetaSchema }),
);

cat non ha default: se il CPT non è configurato o il meta manca, lo schema fallisce subito. È una scelta: meglio un errore esplicito che una categoria vuota silenziosa.

Mapper:

export function mapWordPressEventoToEvento(
wp: WordPressEvento,
media: Map<string, string>,
): Evento {
return {
id: wp.id,
slug: wp.slug,
title: wp.title.rendered,
sortDate: wp.meta.sort_date,
image: media.get(wp.meta.image) ?? "",
// ...
};
}

Loader (src/cms/wordpress/wordpress-loader.ts):

  1. store.clear();
  2. GET dell’endpoint con per_page=100 e _fields ridotti;
  3. schema.parse(json);
  4. risoluzione media: una sola chiamata a /wp/v2/media?include=id1,id2,...;
  5. map per ogni item e store.set({ id: slug, data }).
Valore Comportamento
false (CI, staging, produzione) qualsiasi errore di contratto fa fallire la build
true (solo sviluppo locale) logga un warning e lascia la collezione vuota

Con fallback attivo il sito si vede, ma le sezioni mancano. Serve per lavorare senza WordPress, non per “aggiustare” una build rossa in produzione.

Il campo in admin contiene un ID media. Il loader raccoglie tutti gli ID della collezione e fa una richiesta:

/wp-json/wp/v2/media?per_page=100&include=42,43,44&_fields=id,source_url

Senza questo batch servirebbe una richiesta per immagine. Con 60 eventi e 2 immagini ciascuno sarebbero 120 richieste invece di 1.

tests/wordpress-schema.test.ts valida fixture JSON reali contro gli schema e i mapper. tests/wordpress-loader.test.ts simula fetch e verifica i tre casi:

  • contratto valido → entry nello store;
  • contratto rotto + fallback false → errore;
  • contratto rotto + fallback true → warning, store vuoto.

Quando cambi un campo del mu-plugin:

  1. aggiorna lo schema Zod;
  2. aggiorna il mapper e il modello;
  3. aggiorna la fixture in tests/fixtures/;
  4. esegui npm run test.
Sintomo Causa
Invalid input: expected string, received undefined un meta non è registrato nel mu-plugin, o il JSON salvato è malformato
meta: {} il CPT non ha custom-fields tra i supports, oppure i permalink non sono stati rigenerati
Immagine vuota campo compilato con URL invece che ID media
Tutte le collezioni vuote WordPress non raggiungibile o WORDPRESS_API_BASE_URL sbagliata
Build rossa dopo una rinomina schema e mu-plugin non sono allineati
  • per_page=100, senza paginazione: oltre 100 elementi per CPT il loader ne legge solo 100. Per superare il limite si aggiunge un ciclo su page nel loader (while (items.length === 100) { page++; ... }).
  • Nessuna cache tra build: ogni build rifà le stesse richieste. Sono ~14 richieste per build, quindi va bene.
  • Nessun preview: il contenuto si vede dopo il rebuild.
  • Nessun contenuto HTML arbitrario: i campi sono testo, non HTML dell’editor (salvo scelta esplicita e sanitizzazione).

Questi limiti sono deliberati. Se un progetto li supera, la soluzione va progettata, non improvvisata nel componente.