JavaScript API Reference
EmDash exports functions for querying content and working with preview, settings, menus, taxonomies, widget areas, sections, and search.
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. The following example loads all posts and checks for an error:
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”The options parameter accepts the following filter:
interface CollectionFilter { status?: "draft" | "published" | "archived"; limit?: number; where?: Record<string, string | string[]>; // Filter by field or taxonomy}Returns
Section titled “Returns”The function resolves to a CollectionResult:
interface CollectionResult<T> { entries: ContentEntry<T>[]; // Empty array if error or none found error?: Error; // Set if query failed}Examples
Section titled “Examples”The following examples filter by status and taxonomy, limit results, and handle errors:
// 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. The following example loads a post and redirects when it is missing:
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 |
options | { locale?: string } | Optional. Locale for slug resolution |
Preview mode is handled automatically — the middleware detects _preview tokens and serves draft content via AsyncLocalStorage. The optional options parameter only accepts a locale for slug resolution; preview state requires no parameter.
Returns
Section titled “Returns”The function resolves to an EntryResult:
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”The following examples fetch by slug and ID, read preview state, and distinguish errors from not-found:
// 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”Query functions return entries in the following shape:
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
Preview system
Section titled “Preview system”generatePreviewToken()
Section titled “generatePreviewToken()”Generate a preview token for draft content. The following example creates a token that expires in one hour:
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 and read its payload:
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 whether a request includes a preview token, then read it:
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.url)) { const token = getPreviewToken(Astro.url); // 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”Read site-wide settings with getSiteSettings and getSiteSetting:
import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settingsconst settings = await getSiteSettings();
// Get single settingconst title = await getSiteSetting("title");Settings are read-only from the runtime API. Use the admin API to update them.
Fetch navigation menus and iterate their items, including nested children:
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”Fetch taxonomy terms, a single term, an entry’s terms, or entries by term:
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”Fetch widget areas and the widgets they contain:
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”Fetch sections and filter them:
import { getSection, getSections } from "emdash";
// Get all sections (paginated)const { items, nextCursor } = await getSections();
// Filter sectionsconst { items: themeSections } = await getSections({ source: "theme" });const { items: results } = await getSections({ search: "newsletter" });
// Get a single section by slugconst cta = await getSection("newsletter-cta");getSections(options?) returns { items: Section[]; nextCursor?: string }. Options are source ("theme" | "user" | "import"), search, limit (default 50, max 100), and cursor.
Search
Section titled “Search”Run a global search across collections. Results include highlighted snippets:
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);});Error handling
Section titled “Error handling”EmDash exports error classes for handling specific failures. The following example catches validation and schema errors:
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); }}