Skip to content

JavaScript API Reference

EmDash exports functions for querying content and working with preview, settings, menus, taxonomies, widget areas, sections, and search.

EmDash’s query functions follow Astro’s live content collections pattern, returning { entries, error } or { entry, error } for graceful error handling.

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);
}
ParameterTypeDescription
collectionstringCollection slug
optionsCollectionFilterOptional filter 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
}

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
}

The following examples filter by status and taxonomy, limit results, and handle errors:

// 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 });
}

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");
}
ParameterTypeDescription
collectionstringCollection slug
slugOrIdstringEntry 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.

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
}

The following examples fetch by slug and ID, read preview state, and distinguish errors from 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");
}

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 identifier
  • slug - URL-friendly identifier
  • status - “draft” | “published” | “archived”
  • createdAt - ISO timestamp
  • updatedAt - ISO timestamp
  • publishedAt - ISO timestamp or null
  • Plus all custom fields defined in your collection schema

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
});

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"
}

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
}

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 ProseMirror
const prosemirrorDoc = portableTextToProsemirror(portableText);

Read site-wide settings with getSiteSettings and getSiteSetting:

import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settings
const settings = await getSiteSettings();
// Get single setting
const 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 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));
});
}

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 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");

Fetch widget areas and the widgets they contain:

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);
});
}

Fetch sections and filter them:

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?) returns { items: Section[]; nextCursor?: string }. Options are source ("theme" | "user" | "import"), search, limit (default 50, max 100), and cursor.

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);
});

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);
}
}