EmDash exporte des fonctions pour interroger le contenu, gérer les médias et travailler avec la base de données.
Content Queries
Les fonctions de requête d’EmDash suivent le modèle des live content collections d’Astro et renvoient { entries, error } ou { entry, error } pour une gestion d’erreurs propre.
getEmDashCollection()
Récupérer toutes les entrées d’une collection.
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 | Slug de la collection |
options | CollectionFilter | Options de filtre facultatives |
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()
Récupérer une entrée par slug ou ID.
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 | Slug de la collection |
slugOrId | string | Slug ou ID de l’entrée |
Le mode aperçu est géré automatiquement — le middleware détecte les jetons _preview et sert les brouillons via AsyncLocalStorage. Aucun paramètre options n’est nécessaire.
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
L’entrée renvoyée par les fonctions de requête :
interface ContentEntry<T = Record<string, unknown>> {
id: string;
data: T;
edit: EditProxy; // Visual editing annotations
}
Le proxy edit fournit des annotations pour l’édition visuelle. Étalez-le sur les éléments pour l’édition en ligne : {...entry.edit.title}. En production, il ne produit aucune sortie.
L’objet data contient tous les champs de contenu plus les champs système :
id— Identifiant uniqueslug— Identifiant adapté aux URLstatus—"draft"|"published"|"archived"createdAt— Horodatage ISOupdatedAt— Horodatage ISOpublishedAt— Horodatage ISO ou null- Plus tous les champs personnalisés définis dans le schéma de la collection
Database Functions
createDatabase()
Créer une connexion à la base de données.
import { createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
runMigrations()
Exécuter les migrations en attente.
import { createDatabase, runMigrations } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const { applied } = await runMigrations(db);
console.log(`Applied ${applied.length} migrations`);
getMigrationStatus()
Vérifier l’état des migrations.
import { createDatabase, getMigrationStatus } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const status = await getMigrationStatus(db);
// { applied: ["0001_core", ...], pending: [] }
Repositories
Accès bas niveau aux données via des dépôts.
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
Gestion programmatique du schéma.
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()
Générer un jeton d’aperçu pour le contenu brouillon.
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({
contentId: "posts:01HXK5MZSN...",
secret: process.env.EMDASH_ADMIN_SECRET,
expiresIn: 3600, // 1 hour
});
verifyPreviewToken()
Vérifier un jeton d’aperçu.
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()
Vérifier si la requête inclut un jeton d’aperçu.
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.request)) {
const token = getPreviewToken(Astro.request);
// Verify and show preview content
}
Content Converters
Convertir entre Portable Text et 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
import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settings
const settings = await getSiteSettings();
// Get single setting
const title = await getSiteSetting("siteTitle");
Les réglages sont en lecture seule via l’API runtime. Utilisez l’API d’administration pour les mettre à jour.
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 exporte des classes d’erreur pour des échecs spécifiques :
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);
}
}