EmDash exportiert Funktionen zum Abfragen von Inhalten, Verwalten von Medien und Arbeiten mit der Datenbank.
Content Queries
Die Abfragefunktionen von EmDash folgen dem Muster der Live Content Collections von Astro und liefern { entries, error } bzw. { entry, error } für sauberes Fehlerhandling.
getEmDashCollection()
Alle Einträge einer Collection abrufen.
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 | Optionale Filteroptionen |
Options
interface CollectionFilter {
status?: "draft" | "published" | "archived";
limit?: number;
where?: Record<string, string | string[]>; // Filter by field or taxonomy
}
Returns
interface CollectionResult<T> {
entries: ContentEntry<T>[]; // Empty array if error or none found
error?: Error; // Set if query failed
}
Examples
// 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" },
});
// Handle errors
const { entries, error } = await getEmDashCollection("posts");
if (error) {
return new Response("Server error", { status: 500 });
}
getEmDashEntry()
Einen einzelnen Eintrag per Slug oder ID abrufen.
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 | Slug oder ID des Eintrags |
Der Preview-Modus wird automatisch behandelt — die Middleware erkennt _preview-Tokens und liefert Draft-Inhalte über AsyncLocalStorage. Es ist kein Optionsparameter nötig.
Returns
interface EntryResult<T> {
entry: ContentEntry<T> | null; // null if not found
error?: Error; // Set only for actual errors, not "not found"
isPreview: boolean; // true if draft content is being served
}
Examples
// 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");
}
Content Types
ContentEntry
Der von Abfragefunktionen zurückgegebene Eintrag:
interface ContentEntry<T = Record<string, unknown>> {
id: string;
data: T;
edit: EditProxy; // Visual editing annotations
}
Der edit-Proxy liefert Annotationen für visuelles Bearbeiten. Verteilen Sie ihn auf Elemente für Inline-Editing: {...entry.edit.title}. In Production erzeugt er keine Ausgabe.
Das data-Objekt enthält alle Inhaltsfelder plus Systemfelder:
id— Eindeutige IDslug— URL-tauglicher Identifierstatus—"draft"|"published"|"archived"createdAt— ISO-ZeitstempelupdatedAt— ISO-ZeitstempelpublishedAt— ISO-Zeitstempel oder null- Plus alle in Ihrem Collection-Schema definierten benutzerdefinierten Felder
Database Functions
createDatabase()
Eine Datenbankverbindung erstellen.
import { createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
runMigrations()
Ausstehende Datenbankmigrationen ausführen.
import { createDatabase, runMigrations } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const { applied } = await runMigrations(db);
console.log(`Applied ${applied.length} migrations`);
getMigrationStatus()
Migrationsstatus prüfen.
import { createDatabase, getMigrationStatus } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const status = await getMigrationStatus(db);
// { applied: ["0001_core", ...], pending: [] }
Repositories
Low-Level-Datenzugriff über Repositories.
ContentRepository
import { ContentRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const repo = new ContentRepository(db);
// Find many
const { items, nextCursor } = await repo.findMany("posts", {
limit: 10,
where: { status: "published" },
});
// Find by ID
const post = await repo.findById("posts", "01HXK5MZSN...");
// Create
const newPost = await repo.create({
type: "posts",
slug: "new-post",
data: { title: "New Post", content: [] },
status: "draft",
});
// Update
const updated = await repo.update("posts", "01HXK5MZSN...", {
data: { title: "Updated Title" },
});
// Delete
await repo.delete("posts", "01HXK5MZSN...");
MediaRepository
import { MediaRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const repo = new MediaRepository(db);
// List media
const { items } = await repo.findMany({ limit: 20 });
// Get by ID
const media = await repo.findById("01HXK5MZSN...");
// Create (after upload)
const newMedia = await repo.create({
filename: "photo.jpg",
mimeType: "image/jpeg",
size: 12345,
storageKey: "uploads/photo.jpg",
});
Schema Registry
Programmatische Schema-Verwaltung.
import { SchemaRegistry, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const registry = new SchemaRegistry(db);
// List collections
const collections = await registry.listCollections();
// Get collection with fields
const postsSchema = await registry.getCollectionWithFields("posts");
// Create collection
await registry.createCollection({
slug: "products",
label: "Products",
labelSingular: "Product",
supports: ["drafts", "revisions"],
});
// Add field
await registry.createField("products", {
slug: "price",
label: "Price",
type: "number",
required: true,
});
Preview System
generatePreviewToken()
Preview-Token für Draft-Inhalte erzeugen.
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({
contentId: "posts:01HXK5MZSN...",
secret: process.env.EMDASH_ADMIN_SECRET,
expiresIn: 3600, // 1 hour
});
verifyPreviewToken()
Preview-Token verifizieren.
import { verifyPreviewToken } from "emdash";
const result = await verifyPreviewToken({
token,
secret: process.env.EMDASH_ADMIN_SECRET,
});
if (result.valid) {
const { cid, exp, iat } = result.payload;
// cid is "collection:id" format, e.g. "posts:my-draft-post"
}
isPreviewRequest()
Prüfen, ob eine Anfrage ein Preview-Token enthält.
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.request)) {
const token = getPreviewToken(Astro.request);
// Verify and show preview content
}
Content Converters
Zwischen Portable Text und ProseMirror konvertieren.
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
import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settings
const settings = await getSiteSettings();
// Get single setting
const title = await getSiteSetting("siteTitle");
Über die Runtime-API sind Einstellungen nur lesbar. Zum Aktualisieren die Admin-API verwenden.
Menus
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));
});
}
Taxonomies
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");
Widget Areas
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);
});
}
Sections
import { getSection, getSections, getSectionCategories } from "emdash";
// Get all sections
const sections = await getSections();
// Filter sections
const heroes = await getSections({ category: "hero" });
const themeSections = await getSections({ source: "theme" });
const results = await getSections({ search: "newsletter" });
// Get single section
const cta = await getSection("newsletter-cta");
// Get categories
const categories = await getSectionCategories();
Search
import { search, searchCollection } from "emdash";
// Global search across collections
const results = await search("hello world", {
collections: ["posts", "pages"],
status: "published",
limit: 20,
});
// Results include snippets with highlights
results.forEach(result => {
console.log(result.title);
console.log(result.snippet); // Contains <mark> tags
console.log(result.score);
});
// Collection-specific search
const posts = await searchCollection("posts", "typescript", {
limit: 10,
});
Error Handling
EmDash exportiert Fehlerklassen für spezifische Fehlerfälle:
import {
EmDashDatabaseError,
EmDashValidationError,
EmDashStorageError,
SchemaError,
} from "emdash";
try {
await repo.create({ ... });
} catch (error) {
if (error instanceof EmDashValidationError) {
console.error("Validation failed:", error.message);
}
if (error instanceof SchemaError) {
console.error("Schema error:", error.code, error.details);
}
}