Riferimento API JavaScript

In questa pagina

Questa pagina descrive l’API pubblica che le pagine, i layout e i componenti Astro usano per leggere e presentare un sito EmDash. Non elenca ogni export della radice del pacchetto emdash: i repository del database, gli handler delle API, le utility di migrazione, le API per creare plugin e altri elementi interni del server hanno riferimenti separati o sono pensati per il codice di integrazione del framework.

Il contratto API dei modelli di sito di questo riferimento elenca ogni funzione che i modelli di sito mantenuti da EmDash importano da emdash. Gli import di soli tipi, come MediaValue, appartengono alla documentazione del relativo modello di dati. Le funzioni correlate sono documentate nelle stesse sezioni quando aiutano chi crea un sito, ma la pagina non copre export della radice non correlati.

Contratto API dei modelli di sito

I modelli mantenuti chiamano questi helper di runtime:

FunzioneUsata per
decodeSlugDecodificare un parametro di route dinamico prima della ricerca
getEmDashCollectionLeggere e filtrare le voci di una collection
getEmDashEntryLeggere una voce per ID o slug
getMenuWithCacheHintRenderizzare un menu di navigazione con invalidazione della cache
getSeoMetaRisolvere i valori del pannello SEO di una voce e i fallback del modello
getSiteSettingsLeggere l’identità pubblica del sito e altre impostazioni globali
getSiteSettingsWithCacheHintLeggere le impostazioni globali con invalidazione della cache
getTaxonomyTermsWithCacheHintRenderizzare filtri di tassonomia con invalidazione della cache
getTermsForEntriesCaricare in batch una tassonomia per un elenco di voci
sanitizeHrefRifiutare schemi URL non sicuri prima di renderizzare link salvati
searchCercare contenuti pubblicati in più collection

Query sui contenuti

Le funzioni di query di EmDash seguono il pattern delle live content collections di Astro e restituiscono { entries, error } o { entry, error } per gestire gli errori in modo graduale.

getEmDashCollection()

Recupera tutte le voci di una collection. L’esempio seguente carica tutti gli articoli e controlla se c’è un errore:

import { getEmDashCollection } from "emdash";

const { entries: posts, error } = await getEmDashCollection("posts");

if (error) {
	console.error("Failed to load posts:", error);
}

Parametri

ParametroTipoDescrizione
collectionstringSlug della collection
optionsCollectionFilterOpzioni di filtro facoltative

Opzioni

Il parametro options accetta il filtro seguente:

interface WhereRange {
	gt?: string;
	gte?: string;
	lt?: string;
	lte?: string;
}

interface CollectionFilter {
	status?: "draft" | "published" | "archived";
	limit?: number;
	cursor?: string; // Keyset pagination — pass a previous `nextCursor`
	offset?: number; // Offset pagination — skip N entries (use with `limit`)
	where?: Record<string, string | string[] | WhereRange>;
	orderBy?: Record<string, "asc" | "desc">;
	locale?: string;
}

cursor e offset si escludono a vicenda. Le chiavi di where possono indicare campi di contenuto, tassonomie o byline; per i confronti ordinati sono disponibili oggetti di intervallo.

Valore restituito

La funzione si risolve in un CollectionResult:

interface CollectionResult<T> {
	entries: ContentEntry<T>[]; // Empty array if error or none found
	error?: Error; // Set if query failed
	cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
	nextCursor?: string; // Cursor for the next keyset page, if any
	hasMore?: boolean; // Whether more entries exist beyond this page (when `limit` is set)
}

Esempi

Gli esempi seguenti filtrano per stato e tassonomia, limitano i risultati e gestiscono gli errori:

// Get all published posts
const { entries: posts } = await getEmDashCollection("posts", {
	status: "published",
});

// Get latest 5 posts
const { entries: latest } = await getEmDashCollection("posts", {
	limit: 5,
	status: "published",
});

// Filter by taxonomy
const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

// Numbered archive page (e.g. /page/3) with offset pagination
const perPage = 20;
const page = Number(Astro.params.page ?? 1);
const { entries: pagePosts, hasMore } = await getEmDashCollection("posts", {
	status: "published",
	limit: perPage,
	offset: (page - 1) * perPage,
	orderBy: { published_at: "desc" },
});

// Handle errors
const { entries, error } = await getEmDashCollection("posts");
if (error) {
	return new Response("Server error", { status: 500 });
}

getEmDashEntry()

Recupera una singola voce per slug o ID. L’esempio seguente carica un articolo e renderizza la pagina 404 del sito quando manca:

import { getEmDashEntry } from "emdash";

const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");

if (!post) {
	return Astro.rewrite("/404");
}

Parametri

ParametroTipoDescrizione
collectionstringSlug della collection
slugOrIdstringSlug o ID della voce
options{ locale?: string; references?: ReferenceSelection }Facoltativo. Locale per la risoluzione dello slug e campi di riferimento da caricare

La modalità anteprima è gestita automaticamente: quando la richiesta ha un token _preview valido, la query serve il contenuto in bozza. Lo stato di anteprima non richiede alcun parametro.

references indica i campi di riferimento da caricare, indicizzati per slug del campo. true richiede la prima pagina con il limite predefinito di 50 voci; la forma a oggetto accetta un limit (al massimo 100) e il cursor di una pagina precedente. I campi omessi non vengono letti, e una chiamata che omette l’opzione non esegue alcuna query sui riferimenti.

Valore restituito

La funzione si risolve in un EntryResult:

interface EntryResult<T, R> {
	entry: ContentEntry<T, R> | null; // null if not found
	error?: Error; // Set only for actual errors, not "not found"
	isPreview: boolean; // true if draft content is being served
	fallbackLocale?: string; // Set when locale fallback returned another locale
	cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
}

Esempi

Gli esempi seguenti recuperano voci per slug e ID, leggono lo stato di anteprima e distinguono gli errori dal «non trovato»:

// Get by slug
const { entry: post } = await getEmDashEntry("posts", "hello-world");

// Get by ID
const { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");

// Preview is automatic — isPreview is true when a valid _preview token is present
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);

// Handle errors vs not-found
if (error) {
	return new Response("Server error", { status: 500 });
}
if (!entry) {
	return Astro.rewrite("/404");
}

getTranslations()

Recupera le traduzioni disponibili di una voce a partire dallo slug della collection e dall’ID del database:

import { getTranslations } from "emdash";

const { translations, error } = await getTranslations("posts", post.data.id);

Il risultato contiene il translationGroup condiviso, un array translations e un error facoltativo. Ogni riepilogo di traduzione include id, locale, slug e status.

resolveEmDashPath()

Risolve un percorso pubblico rispetto ai pattern URL configurati per le collection instradabili:

import { resolveEmDashPath } from "emdash";

const result = await resolveEmDashPath("/blog/hello-world");

if (result) {
	console.log(result.collection, result.entry.data.title);
}

Il risultato contiene la collection corrispondente, l’entry e i params della route. La funzione restituisce null quando nessun pattern URL configurato corrisponde.

getEditMeta()

Legge i metadati di modifica visuale non enumerabili associati a un valore Portable Text:

import { getEditMeta } from "emdash";

const meta = getEditMeta(post.data.content);

Restituisce { collection, id, field } per un valore annotato, oppure undefined quando il valore non ha annotazioni.

Tipi di contenuto

ContentEntry

Le funzioni di query restituiscono le voci nella forma seguente:

interface ContentEntry<T = Record<string, unknown>, R = ReferencePages> {
	id: string;
	data: T;
	references?: R; // One page per reference field requested; absent otherwise
	edit: EditProxy; // Visual editing annotations
}

Il proxy edit fornisce annotazioni per la modifica visuale. Distribuiscilo sugli elementi per abilitare la modifica in linea: {...entry.edit.title}. Fuori dalla modalità di modifica non produce alcun output.

L’oggetto data contiene tutti i campi di contenuto più i campi di sistema:

  • id - Identificatore univoco
  • slug - Identificatore adatto agli URL
  • status - “draft” | “published” | “archived”
  • createdAt - Data di creazione come Date
  • updatedAt - Data dell’ultimo aggiornamento come Date
  • publishedAt - Data di pubblicazione come Date, oppure null; viene mantenuta quando il contenuto viene rimosso dalla pubblicazione
  • Più tutti i campi personalizzati definiti nello schema della tua collection

ReferencePage

entry.references contiene una pagina per ogni campo di riferimento richiesto a getEmDashEntry, indicizzata per slug del campo:

interface ReferencePage<T = Record<string, unknown>> {
	entries: ContentEntry<T>[];
	nextCursor?: string; // Set when the field holds more entries than the limit
}

Ogni voce viene mappata come una voce caricata direttamente, con un’eccezione: i bylines e i termini di tassonomia non vengono idratati, quindi data.bylines e data.terms sono assenti.

getEmDashReferences()

Recupera una pagina di un singolo campo di riferimento, senza rileggere la voce a cui appartiene. Usala per andare oltre la prima pagina con il nextCursor restituito da quella pagina.

ParametroTipoDescrizione
collectionstringSlug della collection della voce che contiene il campo
slugOrIdstringSlug o ID della voce
fieldstringSlug del campo di riferimento
options{ limit?: number; cursor?: string; locale?: string }Facoltativo. Per impostazione predefinita 50 voci, al massimo 100

L’esempio seguente carica le 20 voci collegate successive:

import { getEmDashReferences } from "emdash";

const { entries, nextCursor, error } = await getEmDashReferences(
	"posts",
	post.id,
	"related_posts",
	{ cursor, limit: 20 },
);

error viene impostato solo per errori reali. Un campo sconosciuto, una voce mancante o una voce che la richiesta non può vedere si risolvono tutti in un array entries vuoto.

Il risultato include anche un cacheHint che indica le righe lette dalla pagina. Una route che mette in cache ciò che renderizza dovrebbe unirlo al proprio hint, come fa getEmDashEntry({ references }) con quello della prima pagina, così che la scrittura su una di quelle voci faccia scadere la pagina.

Helper URL

decodeSlug() e slugify()

Usa decodeSlug() su un parametro di route dinamico prima di passarlo a una query sui contenuti. Restituisce undefined per un parametro mancante e altrimenti applica decodeURIComponent(), che genera un’eccezione se la codifica percentuale è malformata:

function decodeSlug(raw: string | undefined): string | undefined;
import { decodeSlug, getEmDashEntry } from "emdash";

const slug = decodeSlug(Astro.params.slug);
const { entry } = slug ? await getEmDashEntry("posts", slug) : { entry: null };

slugify(value) converte il testo in uno slug minuscolo separato da trattini. Usalo quando un modello deve costruire uno slug a partire da un’etichetta; gli slug delle voci salvate provengono già da EmDash.

sanitizeHref() e isSafeHref()

Questi helper accettano http:, https:, mailto:, tel:, percorsi relativi al sito e frammenti. Rifiutano gli schemi in grado di eseguire script, gli URL relativi al protocollo, le forme che iniziano con una barra rovesciata che i browser risolvono verso un altro sito e i caratteri di controllo. isSafeHref(value) restituisce un booleano. sanitizeHref(value) restituisce l’URL sicuro originale oppure "#" quando il valore è vuoto o non sicuro.

import { sanitizeHref } from "emdash";

const href = sanitizeHref(menuItem.url);

Sistema preview

generatePreviewToken()

Genera un token di anteprima per un contenuto in bozza. L’esempio seguente crea un token che scade tra un’ora:

import { generatePreviewToken } from "emdash";

const token = await generatePreviewToken({
	contentId: "posts:01HXK5MZSN...",
	secret: process.env.EMDASH_PREVIEW_SECRET!,
	expiresIn: 3600, // 1 hour
});

contentId deve usare il formato collection:id. expiresIn accetta secondi o una durata che termina con s, m, h, d o w, e per impostazione predefinita vale "1h". Tieni il segreto di firma sul server.

verifyPreviewToken()

Verifica un token di anteprima e ne legge il payload:

import { verifyPreviewToken } from "emdash";

const result = await verifyPreviewToken({
	token,
	secret: process.env.EMDASH_PREVIEW_SECRET!,
});

if (result.valid) {
	const { cid, exp, iat } = result.payload;
	// cid is "collection:id" format, e.g. "posts:my-draft-post"
}

Passa token oppure url insieme al secret di firma. Un token non valido restituisce { valid: false, error }, dove error è "none", "malformed", "invalid" o "expired".

parseContentId()

Divide in due parti il valore collection:id del payload di un’anteprima:

import { parseContentId } from "emdash";

const parsed = parseContentId(result.payload.cid);

Restituisce { collection, id } e genera un’eccezione quando il valore non ha il separatore due punti.

getPreviewUrl() e buildPreviewUrl()

getPreviewUrl() crea e firma un URL di anteprima. Accetta collection, id e secret, più i valori facoltativi expiresIn, baseUrl, pathPattern e locale:

import { getPreviewUrl } from "emdash";

const previewUrl = await getPreviewUrl({
	collection: "posts",
	id: post.id,
	secret: process.env.EMDASH_PREVIEW_SECRET!,
	pathPattern: "/blog/{id}",
});

Senza baseUrl restituisce un URL relativo al sito. Usa buildPreviewUrl({ path, token, baseUrl? }) quando esiste già un token.

isPreviewRequest()

Controlla se una richiesta include un token di anteprima, poi lo legge:

import { isPreviewRequest, getPreviewToken } from "emdash";

if (isPreviewRequest(Astro.url)) {
	const token = getPreviewToken(Astro.url);
	// Verify and show preview content
}

getPreviewToken() restituisce il parametro di query _preview oppure null se è assente. Il middleware di EmDash verifica le normali richieste di anteprima e fornisce automaticamente lo stato di anteprima a getEmDashEntry(); questi helper servono per route di anteprima personalizzate e strumenti.

Convertitori di contenuto

Converti tra i formati Portable Text e ProseMirror:

import { prosemirrorToPortableText, portableTextToProsemirror } from "emdash";

// From ProseMirror (editor) to Portable Text (storage)
const portableText = prosemirrorToPortableText(prosemirrorDoc);

// From Portable Text to ProseMirror
const prosemirrorDoc = portableTextToProsemirror(portableText);

Impostazioni del sito

Leggi le impostazioni dell’intero sito con getSiteSettings e getSiteSetting:

function getSiteSettings(): Promise<Partial<SiteSettings>>;
function getSiteSetting<K extends SiteSettingKey>(key: K): Promise<SiteSettings[K] | undefined>;
import { getSiteSettings, getSiteSetting } from "emdash";

// Get all settings
const settings = await getSiteSettings();

// Get single setting
const title = await getSiteSetting("title");

Le impostazioni sono di sola lettura dall’API di runtime. Usa l’API di amministrazione per aggiornarle.

getSiteSettings() restituisce un oggetto parziale perché le chiavi non impostate vengono omesse. Le impostazioni multimediali, come il logo e la favicon del sito, vengono risolte in oggetti di riferimento ai media prima che la funzione restituisca il risultato.

getSiteSettingsWithCacheHint() restituisce { data, cacheHint }. Passa l’hint a Astro.cache.set() quando una cache di route di Astro deve essere invalidata dopo una modifica alle impostazioni del sito.

SEO

Risolvi i valori del pannello SEO e i fallback del contenuto per una pagina con getSeoMeta():

function getSeoMeta<T>(content: SeoContentInput<T>, options?: SeoMetaOptions): SeoMeta;

interface SeoMetaOptions {
	siteTitle?: string;
	siteUrl?: string;
	titleSeparator?: string; // Default: " | "
	path?: string;
	defaultOgImage?: string;
	defaultTitle?: string;
	defaultDescription?: string;
}

interface SeoMeta {
	title: string;
	description: string | null;
	ogTitle: string;
	ogDescription: string | null;
	ogImage: string | null;
	canonical: string | null;
	robots: string | null;
}
import { getSeoMeta } from "emdash";

const meta = getSeoMeta(post, {
	siteTitle: "Example Blog",
	siteUrl: "https://example.com",
	path: `/blog/${post.data.slug}`,
});

Restituisce il titolo risolto, la descrizione, i valori Open Graph, l’URL canonico e il valore robots. getContentSeo(content) restituisce l’oggetto SEO grezzo senza applicare i fallback del modello.

Per i contenuti tradotti, getHreflangAlternates(collection, entryId, { siteUrl? }) restituisce le varianti di locale pubblicate e instradabili come oggetti { hreflang, href } e aggiunge una voce x-default. Restituisce un array vuoto quando l’internazionalizzazione è disabilitata, la voce corrente è contrassegnata come noindex o non è disponibile alcun URL assoluto del sito.

Commenti

Recupera i commenti approvati di una voce e il loro numero:

import { getCommentCount, getComments } from "emdash";

const { items: comments, total } = await getComments({
	collection: "posts",
	contentId: post.data.id,
	threaded: true,
	reactions: true,
	sort: "best",
});

const count = await getCommentCount("posts", post.data.id);

threaded annida le risposte sotto il commento padre. Il valore predefinito di sort è "oldest"; "best" ordina i commenti di primo livello in base alle reazioni e abilita automaticamente il conteggio delle reazioni. Le query dei commenti renderizzate lato server restituiscono al massimo 500 commenti approvati; usa l’API REST quando un client ha bisogno della paginazione.

Recupera i menu di navigazione e scorri le loro voci, compresi i figli annidati:

function getMenu(name: string, options?: { locale?: string }): Promise<Menu | null>;
function getMenus(options?: { locale?: string }): Promise<MenuSummary[]>;
import { getMenu, getMenus } from "emdash";

// Get all menus
const menus = await getMenus();

// Get specific menu with items
const primaryMenu = await getMenu("primary");

if (primaryMenu) {
	primaryMenu.items.forEach(item => {
		console.log(item.label, item.url);
		// Nested items for dropdowns
		item.children.forEach(child => console.log("  -", child.label));
	});
}

getMenu(name, { locale? }) segue la catena di fallback delle locale configurata. getMenus({ locale? }) elenca i riepiloghi dei menu per la locale della richiesta risolta o per quella configurata; senza internazionalizzazione elenca tutte le locale. getMenuWithCacheHint() restituisce { data, cacheHint } per le route che usano la cache di Astro.

Bylines

Recupera il profilo di un autore per ID o slug, oppure elenca le voci attribuite a un byline:

import { getByline, getBylineBySlug, getEntriesByByline } from "emdash";

const profile = await getBylineBySlug("jane-doe", { locale: "en" });
const posts = profile
	? await getEntriesByByline("posts", profile.translationGroup ?? profile.id)
	: [];

getByline(id) restituisce un profilo oppure null. La ricerca per slug accetta una locale facoltativa e segue la catena di fallback delle locale. Le query sui contenuti idratano già i crediti ordinati di una voce in entry.data.bylines; usa questi helper autonomi per le pagine degli autori e gli archivi dei bylines.

Tassonomie

Recupera termini di tassonomia, un singolo termine, i termini di una voce o le voci per termine:

function getTaxonomyTerms(
	taxonomyName: string,
	options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm[]>;

function getTerm(
	taxonomyName: string,
	slug: string,
	options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm | null>;

function getTermsForEntries(
	collection: string,
	entryIds: string[],
	taxonomyName: string,
	options?: { locale?: string },
): Promise<Map<string, TaxonomyTerm[]>>;
import { getTaxonomyTerms, getTerm, getEntryTerms, getEntriesByTerm } from "emdash";

// Get all terms for a taxonomy (tree structure for hierarchical)
const categories = await getTaxonomyTerms("category");

// Get single term
const news = await getTerm("category", "news");

// Get terms assigned to a content entry
const postCategories = await getEntryTerms("posts", "post-123", "category");

// Get entries with a specific term
const newsPosts = await getEntriesByTerm("posts", "category", "news");

getTaxonomyDefs({ locale? }) elenca una definizione per tassonomia, e getTaxonomyDef(name, { locale? }) restituisce una definizione, oppure null quando nessuna locale definisce la tassonomia. Entrambe risolvono una tassonomia in ogni locale; Tradurre tassonomie e termini spiega quale etichetta di locale usano. Le ricerche dei termini seguono la catena di fallback configurata.

getTaxonomyTerms(name, { locale?, includeCounts? }) restituisce un albero per le tassonomie gerarchiche e include per impostazione predefinita il numero di voci visibili. Passa includeCounts: false quando il modello non mostra i conteggi. Le query sui contenuti idratano anche i termini assegnati nel data.terms di ogni voce; usa getEntryTerms() quando hai solo il nome di una collection e l’ID di una voce.

Per le pagine di archivio che mostrano i termini accanto a molte voci, raggruppa la ricerca in batch invece di chiamare getEntryTerms() in un ciclo:

import { getAllTermsForEntries, getTermsForEntries } from "emdash";

const termsByPost = await getTermsForEntries(
	"posts",
	posts.map(post => post.data.id),
	"category",
);

const allTermsByPost = await getAllTermsForEntries(
	"posts",
	posts.map(post => post.data.id),
);

getTermsForEntries() restituisce una mappa dall’ID della voce ai termini della tassonomia richiesta. getAllTermsForEntries() restituisce una mappa dall’ID della voce ai termini raggruppati per nome di tassonomia. getTaxonomyTermsWithCacheHint() restituisce { data, cacheHint } per una route con cache di Astro.

Aree widget

Recupera le aree widget e i widget che contengono:

import { getWidgetArea, getWidgetAreas } from "emdash";

// Get all widget areas
const areas = await getWidgetAreas();

// Get specific widget area with widgets
const sidebar = await getWidgetArea("sidebar");

if (sidebar) {
	sidebar.widgets.forEach(widget => {
		console.log(widget.type, widget.title);
	});
}

getWidgetAreaWithCacheHint(name) restituisce { data, cacheHint } per le route che usano la cache di Astro. Le aree widget e i loro widget sono ordinati secondo l’ordinamento configurato.

Sections

Recupera le section e filtrale:

import { getSection, getSections } from "emdash";

// Get all sections (paginated)
const { items, nextCursor } = await getSections();

// Filter sections
const { items: themeSections } = await getSections({ source: "theme" });
const { items: results } = await getSections({ search: "newsletter" });

// Get a single section by slug
const cta = await getSection("newsletter-cta");

getSections(options?) restituisce { items: Section[]; nextCursor?: string }. Le opzioni sono source ("theme" | "user" | "import"), search, limit (predefinito 50, massimo 100) e cursor.

Ricerca

Esegui una ricerca globale in più collection. I risultati includono snippet evidenziati:

function search(query: string, options?: SearchOptions): Promise<SearchResponse>;

interface SearchOptions {
	collections?: string[]; // Default: every searchable collection
	status?: string; // Default: "published"
	locale?: string; // Default: all locales
	limit?: number; // Default: 20
	cursor?: string;
	scope?: "all" | "title"; // Default: "all"
}

interface SearchResponse {
	items: SearchResult[];
	nextCursor?: string;
}
import { search } from "emdash";

const results = await search("hello world", {
	collections: ["posts", "pages"],
	status: "published",
	limit: 20,
});

// search() resolves to { items, nextCursor? }
results.items.forEach(result => {
	console.log(result.title);
	console.log(result.snippet); // Contains <mark> tags
	console.log(result.score);
});

// Paginate: pass the previous nextCursor back as `cursor` to get the next page.
// nextCursor is undefined once there are no more results.
if (results.nextCursor) {
	const next = await search("hello world", {
		collections: ["posts", "pages"],
		limit: 20,
		cursor: results.nextCursor,
	});
}

Con scope: "title" la query corrisponde solo al campo titolo di ogni collection invece che all’intero testo indicizzato: è utile per selettori e completamento automatico, dove le corrispondenze nel corpo del testo risulterebbero sorprendenti. Le collection il cui campo titolo non è indicizzato per la ricerca non restituiscono risultati con questo ambito.

Gestione errori

Le query sui contenuti restituiscono gli errori operativi nel proprio risultato invece di generarli. Una voce mancante non è un errore operativo: entry è null e error resta indefinito.

const { entry, error } = await getEmDashEntry("posts", slug);

if (error) {
	return new Response("Content could not be loaded", { status: 500 });
}

if (!entry) {
	return Astro.rewrite("/404");
}

Gli helper senza un involucro di risultato possono generare eccezioni quando l’input non è valido o un’operazione sul database fallisce. Gestisci questi errori al confine della route quando la pagina può offrire un fallback utile. Le classi di errore dei repository e degli handler usate dalle integrazioni server di livello più basso sono fuori dall’ambito di questa API dei modelli di sito.