JavaScript API Reference
EmDash exports functions for querying content, managing media, and working with the database.
Content Queries
Section titled “Content Queries”EmDash’s query functions follow Astro’s live content collections pattern, returning { entries, error } or { entry, error } for graceful error handling.
getEmDashCollection()
Section titled “getEmDashCollection()”Fetch all entries from a collection.
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) { console.error("Failed to load posts:", error);}Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
collection | string | Collection slug |
options | CollectionFilter | Optional filter options |
Options
Section titled “Options”interface CollectionFilter { status?: "draft" | "published" | "archived"; limit?: number; where?: Record<string, string | string[]>; // Filter by field or taxonomy}Returns
Section titled “Returns”interface CollectionResult<T> { entries: ContentEntry<T>[]; // Empty array if error or none found error?: Error; // Set if query failed}Examples
Section titled “Examples”// Get all published postsconst { entries: posts } = await getEmDashCollection("posts", { status: "published",});
// Get latest 5 postsconst { entries: latest } = await getEmDashCollection("posts", { limit: 5, status: "published",});
// Filter by taxonomyconst { entries: newsPosts } = await getEmDashCollection("posts", { status: "published", where: { category: "news" },});
// Handle errorsconst { entries, error } = await getEmDashCollection("posts");if (error) { return new Response("Server error", { status: 500 });}getEmDashEntry()
Section titled “getEmDashEntry()”Fetch a single entry by slug or ID.
import { getEmDashEntry } from "emdash";
const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");
if (!post) { return Astro.redirect("/404");}Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
collection | string | Collection slug |
slugOrId | string | Entry slug or ID |
Preview mode is handled automatically — the middleware detects _preview tokens and serves draft content via AsyncLocalStorage. No options parameter is needed.
Returns
Section titled “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
Section titled “Examples”// Get by slugconst { entry: post } = await getEmDashEntry("posts", "hello-world");
// Get by IDconst { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");
// Preview is automatic — isPreview is true when a valid _preview token is presentconst { entry, isPreview, error } = await getEmDashEntry("posts", slug);
// Handle errors vs not-foundif (error) { return new Response("Server error", { status: 500 });}if (!entry) { return Astro.redirect("/404");}Content Types
Section titled “Content Types”ContentEntry
Section titled “ContentEntry”The entry returned by query functions:
interface ContentEntry<T = Record<string, unknown>> { id: string; data: T; edit: EditProxy; // Visual editing annotations}The edit proxy provides visual editing annotations. Spread it onto elements to enable inline editing: {...entry.edit.title}. In production, this produces no output.
The data object contains all content fields plus system fields:
id- Unique identifierslug- URL-friendly identifierstatus- “draft” | “published” | “archived”createdAt- ISO timestampupdatedAt- ISO timestamppublishedAt- ISO timestamp or null- Plus all custom fields defined in your collection schema
Database Functions
Section titled “Database Functions”createDatabase()
Section titled “createDatabase()”Create a database connection.
import { createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });runMigrations()
Section titled “runMigrations()”Run pending database migrations.
import { createDatabase, runMigrations } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const { applied } = await runMigrations(db);console.log(`Applied ${applied.length} migrations`);getMigrationStatus()
Section titled “getMigrationStatus()”Check migration status.
import { createDatabase, getMigrationStatus } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const status = await getMigrationStatus(db);// { applied: ["0001_core", ...], pending: [] }Repositories
Section titled “Repositories”Low-level data access through repositories.
ContentRepository
Section titled “ContentRepository”import { ContentRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const repo = new ContentRepository(db);
// Find manyconst { items, nextCursor } = await repo.findMany("posts", { limit: 10, where: { status: "published" },});
// Find by IDconst post = await repo.findById("posts", "01HXK5MZSN...");
// Createconst newPost = await repo.create({ type: "posts", slug: "new-post", data: { title: "New Post", content: [] }, status: "draft",});
// Updateconst updated = await repo.update("posts", "01HXK5MZSN...", { data: { title: "Updated Title" },});
// Deleteawait repo.delete("posts", "01HXK5MZSN...");MediaRepository
Section titled “MediaRepository”import { MediaRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const repo = new MediaRepository(db);
// List mediaconst { items } = await repo.findMany({ limit: 20 });
// Get by IDconst 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
Section titled “Schema Registry”Programmatic schema management.
import { SchemaRegistry, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const registry = new SchemaRegistry(db);
// List collectionsconst collections = await registry.listCollections();
// Get collection with fieldsconst postsSchema = await registry.getCollectionWithFields("posts");
// Create collectionawait registry.createCollection({ slug: "products", label: "Products", labelSingular: "Product", supports: ["drafts", "revisions"],});
// Add fieldawait registry.createField("products", { slug: "price", label: "Price", type: "number", required: true,});Preview System
Section titled “Preview System”generatePreviewToken()
Section titled “generatePreviewToken()”Generate a preview token for draft content.
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({ contentId: "posts:01HXK5MZSN...", secret: process.env.EMDASH_ADMIN_SECRET, expiresIn: 3600, // 1 hour});verifyPreviewToken()
Section titled “verifyPreviewToken()”Verify a preview token.
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()
Section titled “isPreviewRequest()”Check if a request includes a preview token.
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.request)) { const token = getPreviewToken(Astro.request); // Verify and show preview content}Content Converters
Section titled “Content Converters”Convert between Portable Text and ProseMirror formats.
import { prosemirrorToPortableText, portableTextToProsemirror } from "emdash";
// From ProseMirror (editor) to Portable Text (storage)const portableText = prosemirrorToPortableText(prosemirrorDoc);
// From Portable Text to ProseMirrorconst prosemirrorDoc = portableTextToProsemirror(portableText);Site Settings
Section titled “Site Settings”import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settingsconst settings = await getSiteSettings();
// Get single settingconst title = await getSiteSetting("siteTitle");Settings are read-only from the runtime API. Use the admin API to update them.
import { getMenu, getMenus } from "emdash";
// Get all menusconst menus = await getMenus();
// Get specific menu with itemsconst 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
Section titled “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 termconst news = await getTerm("category", "news");
// Get terms assigned to a content entryconst postCategories = await getEntryTerms("posts", "post-123", "category");
// Get entries with a specific termconst newsPosts = await getEntriesByTerm("posts", "category", "news");Widget Areas
Section titled “Widget Areas”import { getWidgetArea, getWidgetAreas } from "emdash";
// Get all widget areasconst areas = await getWidgetAreas();
// Get specific widget area with widgetsconst sidebar = await getWidgetArea("sidebar");
if (sidebar) { sidebar.widgets.forEach(widget => { console.log(widget.type, widget.title); });}Sections
Section titled “Sections”import { getSection, getSections, getSectionCategories } from "emdash";
// Get all sectionsconst sections = await getSections();
// Filter sectionsconst heroes = await getSections({ category: "hero" });const themeSections = await getSections({ source: "theme" });const results = await getSections({ search: "newsletter" });
// Get single sectionconst cta = await getSection("newsletter-cta");
// Get categoriesconst categories = await getSectionCategories();Search
Section titled “Search”import { search, searchCollection } from "emdash";
// Global search across collectionsconst results = await search("hello world", { collections: ["posts", "pages"], status: "published", limit: 20,});
// Results include snippets with highlightsresults.forEach(result => { console.log(result.title); console.log(result.snippet); // Contains <mark> tags console.log(result.score);});
// Collection-specific searchconst posts = await searchCollection("posts", "typescript", { limit: 10,});Error Handling
Section titled “Error Handling”EmDash exports error classes for handling specific failures:
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); }}