Diese Seite beschreibt die öffentliche API, die Astro-Seiten, Layouts und Komponenten zum Lesen und Präsentieren einer EmDash-Site verwenden. Sie listet nicht jeden Export aus dem Paket-Root emdash auf: Datenbank-Repositories, API-Handler, Migrations-Hilfen, Plugin-Autoren-APIs und andere Server-Interna haben eigene Referenzen oder sind für Framework-Integrationscode gedacht.
Der Site-Template-API-Vertrag in dieser Referenz listet jede Funktion auf, die von EmDashs gepflegten Site-Templates aus emdash importiert wird. Reine Typ-Imports wie MediaValue gehören zur jeweiligen Datenmodell-Dokumentation. Verwandte Funktionen werden in denselben Abschnitten dokumentiert, wenn sie Site-Autoren helfen; die Seite behandelt keine unverwandten Root-Exports.
Site-Template-API-Vertrag
Die gepflegten Templates rufen diese Laufzeit-Helfer auf:
| Funktion | Verwendung |
|---|---|
decodeSlug | Dynamischen Route-Parameter vor der Suche dekodieren |
getEmDashCollection | Einträge in einer Collection lesen und filtern |
getEmDashEntry | Einen Eintrag per ID oder Slug lesen |
getMenuWithCacheHint | Navigationsmenü mit Cache-Invalidierung rendern |
getSeoMeta | SEO-Panel-Werte eines Eintrags und Template-Fallbacks auflösen |
getSiteSettings | Öffentliche Site-Identität und andere globale Einstellungen lesen |
getSiteSettingsWithCacheHint | Globale Einstellungen mit Cache-Invalidierung lesen |
getTaxonomyTermsWithCacheHint | Taxonomie-Filter mit Cache-Invalidierung rendern |
getTermsForEntries | Eine Taxonomie für eine Liste von Einträgen batch-laden |
sanitizeHref | Unsichere URL-Schemata vor dem Rendern gespeicherter Links ablehnen |
search | Veröffentlichten Content über Collections hinweg durchsuchen |
Content queries
Die Abfragefunktionen von EmDash folgen dem Muster von Astros Live Content Collections und liefern { entries, error } bzw. { entry, error } für graceful Error Handling.
getEmDashCollection()
Lädt alle Einträge einer Collection. Das folgende Beispiel lädt alle Posts und prüft auf einen Fehler:
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
console.error("Failed to load posts:", error);
}
Parameters
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection slug |
options | CollectionFilter | Optional filter options |
Options
Der Parameter options akzeptiert folgenden Filter:
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 und offset schließen sich gegenseitig aus. Die where-Keys können Content-Felder, Taxonomien oder byline benennen; Range-Objekte stehen für geordnete Vergleiche zur Verfügung.
Returns
Die Funktion löst zu einem CollectionResult auf:
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)
}
Examples
Die folgenden Beispiele filtern nach Status und Taxonomie, begrenzen Ergebnisse und behandeln Fehler:
// 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()
Lädt einen einzelnen Eintrag per Slug oder ID. Das folgende Beispiel lädt einen Post und leitet um, wenn er fehlt:
import { getEmDashEntry } from "emdash";
const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");
if (!post) {
return Astro.redirect("/404");
}
Parameters
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection slug |
slugOrId | string | Entry slug or ID |
options | { locale?: string; references?: ReferenceSelection } | Optional. Locale for slug resolution, and the reference fields to load |
Der Preview-Modus wird automatisch behandelt: Wenn die Anfrage ein gültiges _preview-Token hat, liefert die Abfrage Draft-Inhalt. Für den Preview-Zustand ist kein Parameter nötig.
references benennt die zu ladenden Reference-Felder, keyed by field slug. true fordert die erste Seite mit dem Standard-Limit von 50 Einträgen an; die Objektform nimmt limit (höchstens 100) und den cursor einer vorherigen Seite. Ausgelassene Felder werden nicht gelesen; ohne Option führt der Aufruf keine Reference-Abfragen aus.
Returns
Die Funktion löst zu einem EntryResult auf:
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
}
Examples
Die folgenden Beispiele laden per Slug und ID, lesen den Preview-Zustand und unterscheiden Fehler von Not Found:
// 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.redirect("/404");
}
getTranslations()
Lädt die verfügbaren Übersetzungen eines Eintrags anhand von Collection-Slug und Datenbank-ID:
import { getTranslations } from "emdash";
const { translations, error } = await getTranslations("posts", post.data.id);
Das Ergebnis enthält die gemeinsame translationGroup, ein translations-Array und optional error. Jede Übersetzungs-Zusammenfassung umfasst id, locale, slug und status.
resolveEmDashPath()
Löst einen öffentlichen Pfadnamen gegen die für routable Collections konfigurierten URL-Muster auf:
import { resolveEmDashPath } from "emdash";
const result = await resolveEmDashPath("/blog/hello-world");
if (result) {
console.log(result.collection, result.entry.data.title);
}
Das Ergebnis enthält die gematchte collection, den entry und Route-params. Die Funktion gibt null zurück, wenn kein konfiguriertes URL-Muster passt.
getEditMeta()
Liest die nicht-enumerierbaren Metadaten für visuelles Bearbeiten, die an einen Portable-Text-Wert angehängt sind:
import { getEditMeta } from "emdash";
const meta = getEditMeta(post.data.content);
Sie liefert { collection, id, field } für einen annotierten Wert oder undefined, wenn der Wert keine Annotation hat.
Content types
ContentEntry
Abfragefunktionen liefern Einträge in folgender Form:
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
}
Der edit-Proxy liefert Annotations für visuelles Bearbeiten. Spreaden Sie ihn auf Elemente, um Inline-Bearbeitung zu aktivieren: {...entry.edit.title}. Außerhalb des Edit-Modus erzeugt er keine Ausgabe.
Das Objekt data enthält alle Content-Felder plus Systemfelder:
id- Unique identifierslug- URL-friendly identifierstatus- “draft” | “published” | “archived”createdAt- Creation time as aDateupdatedAt- Last update time as aDatepublishedAt- Publication time as aDate, ornull; retained when content is unpublished- Plus all custom fields defined in your collection schema
ReferencePage
entry.references hält eine Seite pro Reference-Feld, das getEmDashEntry angefordert hat, keyed by field slug:
interface ReferencePage<T = Record<string, unknown>> {
entries: ContentEntry<T>[];
nextCursor?: string; // Set when the field holds more entries than the limit
}
Jeder Eintrag wird wie ein direkt geladener Eintrag gemappt, mit einer Ausnahme: Bylines und Taxonomie-Terms werden nicht hydratisiert; data.bylines und data.terms fehlen daher.
getEmDashReferences()
Lädt eine Seite eines einzelnen Reference-Felds, ohne den Eintrag erneut zu lesen, an dem das Feld hängt. Nutzen Sie es, um mit dem nextCursor dieser Seite über die erste Seite hinaus zu gehen.
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection slug of the entry that holds the field |
slugOrId | string | Entry slug or ID |
field | string | Reference field slug |
options | { limit?: number; cursor?: string; locale?: string } | Optional. Defaults to 50 entries, at most 100 |
Das folgende Beispiel lädt die nächsten 20 verknüpften Einträge:
import { getEmDashReferences } from "emdash";
const { entries, nextCursor, error } = await getEmDashReferences(
"posts",
post.id,
"related_posts",
{ cursor, limit: 20 },
);
error wird nur bei echten Fehlern gesetzt. Unbekanntes Feld, fehlender Eintrag oder ein Eintrag, den die Anfrage nicht sehen darf, führen alle zu einem leeren entries-Array.
Das Ergebnis trägt außerdem einen cacheHint mit den Zeilen, die die Seite gelesen hat. Eine Route, die ihr Rendering cached, sollte ihn in den eigenen Hint mergen – wie getEmDashEntry({ references }) den der ersten Seite einfoldet – damit Schreiben an einen dieser Einträge die Seite invalidiert.
URL helpers
decodeSlug() and slugify()
Verwenden Sie decodeSlug() auf einem dynamischen Route-Parameter, bevor Sie ihn an eine Content-Abfrage übergeben. Sie gibt undefined für einen fehlenden Parameter zurück und wendet sonst decodeURIComponent() an, das bei fehlerhafter Prozent-Kodierung wirft:
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) wandelt Text in einen kleingeschriebenen, bindestrich-getrennten Slug um. Nutzen Sie es, wenn ein Template aus einem Label einen Slug bauen muss; gespeicherte Entry-Slugs kommen bereits von EmDash.
sanitizeHref() and isSafeHref()
Diese Helfer lehnen unsichere Link-Schemata wie javascript: ab. isSafeHref(value) liefert einen Boolean. sanitizeHref(value) liefert die ursprüngliche sichere URL oder "#", wenn der Wert leer oder unsicher ist.
import { sanitizeHref } from "emdash";
const href = sanitizeHref(menuItem.url);
Preview system
generatePreviewToken()
Erzeugt ein Preview-Token für Draft-Inhalt. Das folgende Beispiel erstellt ein Token, das in einer Stunde abläuft:
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({
contentId: "posts:01HXK5MZSN...",
secret: process.env.EMDASH_PREVIEW_SECRET!,
expiresIn: 3600, // 1 hour
});
contentId muss das Format collection:id verwenden. expiresIn akzeptiert Sekunden oder eine Dauer mit Suffix s, m, h, d oder w und standardmäßig "1h". Bewahren Sie das Signing-Secret auf dem Server auf.
verifyPreviewToken()
Verifiziert ein Preview-Token und liest dessen 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"
}
Übergeben Sie entweder token oder url mit dem Signing-secret. Ein ungültiges Token liefert { valid: false, error }, wobei error "none", "malformed", "invalid" oder "expired" ist.
parseContentId()
Teilt den collection:id-Wert einer Preview-Payload in seine beiden Teile:
import { parseContentId } from "emdash";
const parsed = parseContentId(result.payload.cid);
Sie liefert { collection, id } und wirft, wenn der Wert keinen Doppelpunkt enthält.
getPreviewUrl() and buildPreviewUrl()
getPreviewUrl() erstellt und signiert eine Preview-URL. Sie akzeptiert collection, id und secret sowie optional expiresIn, baseUrl, pathPattern und locale:
import { getPreviewUrl } from "emdash";
const previewUrl = await getPreviewUrl({
collection: "posts",
id: post.id,
secret: process.env.EMDASH_PREVIEW_SECRET!,
pathPattern: "/blog/{id}",
});
Ohne baseUrl liefert sie eine site-relative URL. Nutzen Sie buildPreviewUrl({ path, token, baseUrl? }), wenn bereits ein Token existiert.
isPreviewRequest()
Prüft, ob eine Anfrage ein Preview-Token enthält, und liest es:
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.url)) {
const token = getPreviewToken(Astro.url);
// Verify and show preview content
}
getPreviewToken() liefert den Query-Parameter _preview oder null, wenn er fehlt. Die EmDash-Middleware verifiziert normale Preview-Anfragen und stellt den Preview-Zustand getEmDashEntry() automatisch bereit; diese Helfer sind für custom Preview-Routen und Tooling gedacht.
Content converters
Konvertiert zwischen Portable Text und 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);
Site settings
Site-weite Einstellungen lesen Sie mit getSiteSettings und 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");
Einstellungen sind über die Runtime-API read-only. Zum Aktualisieren nutzen Sie die Admin-API.
getSiteSettings() liefert ein partielles Objekt, weil unset Keys fehlen. Media-Einstellungen wie Site-Logo und Favicon werden vor der Rückgabe zu Media-Reference-Objekten aufgelöst.
getSiteSettingsWithCacheHint() liefert { data, cacheHint }. Übergeben Sie den Hint an Astro.cache.set(), wenn ein Astro-Route-Cache nach Änderungen an Site-Einstellungen invalidiert werden soll.
SEO
SEO-Panel-Werte und Content-Fallbacks für eine Seite lösen Sie mit getSeoMeta() auf:
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}`,
});
Sie liefert aufgelösten Titel, Description, Open-Graph-Werte, Canonical-URL und Robots-Wert. getContentSeo(content) liefert das rohe SEO-Objekt ohne Template-Fallbacks.
Für übersetzten Content liefert getHreflangAlternates(collection, entryId, { siteUrl? }) veröffentlichte, routable Locale-Varianten als { hreflang, href }-Objekte und fügt einen x-default-Eintrag hinzu. Sie liefert ein leeres Array, wenn Internationalisierung deaktiviert ist, der aktuelle Eintrag noindex hat oder keine absolute Site-URL verfügbar ist.
Comments
Genehmigte Kommentare und deren Anzahl für einen Eintrag laden:
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 verschachtelt Antworten unter dem Elternkommentar. Standard-sort ist "oldest"; "best" sortiert Top-Level-Kommentare nach Reactions und aktiviert Reaktionszähler automatisch. Serverseitige Kommentar-Abfragen liefern höchstens 500 genehmigte Kommentare; für Pagination nutzen Sie die REST-API.
Menus
Navigationsmenüs laden und deren Items iterieren, einschließlich verschachtelter Kinder:
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? }) folgt der konfigurierten Locale-Fallback-Kette. getMenus({ locale? }) listet Menu-Zusammenfassungen für die aufgelöste Anfrage- oder konfigurierte Locale; ohne Internationalisierung listet es jede Locale. getMenuWithCacheHint() liefert { data, cacheHint } für Routen mit Astro-Cache.
Bylines
Autorenprofil per ID oder Slug laden oder Einträge laden, die einem Byline zugeschrieben sind:
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) liefert ein Profil oder null. Die Slug-Suche akzeptiert optional eine Locale und folgt der Locale-Fallback-Kette. Content-Abfragen hydratisieren die geordneten Credits eines Eintrags bereits in entry.data.bylines; nutzen Sie diese standalone Helfer für Autorenseiten und Byline-Archive.
Taxonomies
Taxonomie-Terms, einen einzelnen Term, die Terms eines Eintrags oder Einträge nach Term laden:
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? }) listet eine Definition pro Taxonomie; getTaxonomyDef(name, { locale? }) liefert eine Definition oder null, wenn keine Locale die Taxonomie definiert. Beide lösen eine Taxonomie in jeder Locale auf; Taxonomien und Terms übersetzen erklärt, welches Locale-Label sie verwenden. Term-Lookups folgen der konfigurierten Fallback-Kette.
getTaxonomyTerms(name, { locale?, includeCounts? }) liefert für hierarchische Taxonomien einen Baum und enthält standardmäßig sichtbare Entry-Counts. Übergeben Sie includeCounts: false, wenn das Template keine Counts rendert. Content-Abfragen hydratisieren zugewiesene Terms auch in data.terms jedes Eintrags; nutzen Sie getEntryTerms(), wenn Sie nur Collection-Name und Entry-ID haben.
Für Archivseiten, die Terms neben vielen Einträgen rendern, bündeln Sie den Lookup statt getEntryTerms() in einer Schleife:
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() liefert eine Map von Entry-ID zu Terms der angeforderten Taxonomie. getAllTermsForEntries() liefert eine Map von Entry-ID zu Terms gruppiert nach Taxonomie-Name. getTaxonomyTermsWithCacheHint() liefert { data, cacheHint } für eine Astro-gecachte Route.
Widget areas
Widget Areas und die enthaltenen Widgets laden:
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) liefert { data, cacheHint } für Routen mit Astro-Cache. Widget Areas und ihre Widgets sind nach konfigurierter Sortierreihenfolge geordnet.
Sections
Sections laden und filtern:
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?) liefert { items: Section[]; nextCursor?: string }. Optionen sind source ("theme" | "user" | "import"), search, limit (Standard 50, max. 100) und cursor.
Search
Globale Suche über Collections. Ergebnisse enthalten hervorgehobene Snippets:
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,
});
}
Mit scope: "title" matcht die Abfrage nur das Title-Feld jeder Collection statt des voll indexierten Texts — nützlich für Picker und Autocomplete, wo Body-Matches überraschen würden. Collections, deren Title-Feld nicht für die Suche indexiert ist, liefern unter diesem Scope keine Ergebnisse.
Error handling
Content-Abfragen liefern operative Fehler im Ergebnis statt zu werfen. Ein fehlender Eintrag ist kein operativer Fehler: entry ist null und error bleibt undefined.
const { entry, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Content could not be loaded", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
Helfer ohne Result-Envelope können werfen, wenn die Eingabe ungültig ist oder eine Datenbankoperation fehlschlägt. Behandeln Sie diese Fehler an der Route-Grenze, wenn die Seite einen sinnvollen Fallback bieten kann. Die Repository- und Handler-Fehlerklassen tieferer Server-Integrationen liegen außerhalb dieser Site-Template-API.