Esta página descreve a API pública usada por páginas, layouts e componentes Astro para ler e apresentar um site EmDash. Não inventaria cada export da raiz do pacote emdash: repositórios de base de dados, handlers de API, utilitários de migração, APIs para autores de plugins e outros internals do servidor têm referências separadas ou destinam-se a código de integração do framework.
O contrato de API de modelos de site nesta referência lista cada função importada de emdash pelos modelos de site mantidos pelo EmDash. Imports apenas de tipos como MediaValue pertencem à documentação do modelo de dados correspondente. Funções relacionadas são documentadas nas mesmas secções quando ajudam o autor do site, mas a página não cobre exports raiz não relacionados.
Contrato de API de modelos de site
Os modelos mantidos invocam estes helpers em runtime:
| Fonction | Uso |
|---|---|
decodeSlug | Descodificar um parâmetro de rota dinâmica antes da pesquisa |
getEmDashCollection | Ler e filtrar entradas numa collection |
getEmDashEntry | Ler uma entrada por ID ou slug |
getMenuWithCacheHint | Renderizar um menu de navegação com invalidação de cache |
getSeoMeta | Resolver valores do painel SEO de uma entrada e fallbacks de modelo |
getSiteSettings | Ler identidade pública do site e outras definições globais |
getSiteSettingsWithCacheHint | Ler definições globais com invalidação de cache |
getTaxonomyTermsWithCacheHint | Renderizar filtros de taxonomia com invalidação de cache |
getTermsForEntries | Carregar em lote uma taxonomia para uma lista de entradas |
sanitizeHref | Rejeitar esquemas URL inseguros antes de renderizar ligações armazenadas |
search | Pesquisar conteúdo publicado em todas as collections |
Consultas de conteúdo
As funções de consulta do EmDash seguem o padrão live content collections d’Astro et devolvem { entries, error } ou { entry, error } para gestão de erros controlada.
getEmDashCollection()
Obtém todas as entradas de uma collection. O exemplo seguinte carrega todos os posts e verifica se há erro:
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 | Optional filter options |
Options
O parâmetro options aceita o seguinte filtro:
interface WhereRange {
gt?: string;
gte?: string;
lt?: string;
lte?: string;
}
interface CollectionFilter {
status?: "draft" | "published" | "archived";
limit?: number;
cursor?: string; // Keyset pagination — pass a previous `nextCursor`
offset?: number; // Offset pagination — skip N entries (use with `limit`)
where?: Record<string, string | string[] | WhereRange>;
orderBy?: Record<string, "asc" | "desc">;
locale?: string;
}
cursor e offset são mutuamente exclusivos. As chaves where podem nomear campos de conteúdo, taxonomias ou byline; objetos de intervalo permitem comparações ordenadas.
Returns
A função resolve-se num CollectionResult:
interface CollectionResult<T> {
entries: ContentEntry<T>[]; // Empty array if error or none found
error?: Error; // Set if query failed
cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
nextCursor?: string; // Cursor for the next keyset page, if any
hasMore?: boolean; // Whether more entries exist beyond this page (when `limit` is set)
}
Examples
Os exemplos seguintes filtram por estado e taxonomia, limitam resultados e tratam erros:
// 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" },
});
// Numbered archive page (e.g. /page/3) with offset pagination
const perPage = 20;
const page = Number(Astro.params.page ?? 1);
const { entries: pagePosts, hasMore } = await getEmDashCollection("posts", {
status: "published",
limit: perPage,
offset: (page - 1) * perPage,
orderBy: { published_at: "desc" },
});
// Handle errors
const { entries, error } = await getEmDashCollection("posts");
if (error) {
return new Response("Server error", { status: 500 });
}
getEmDashEntry()
Obtém uma única entrada por slug ou ID. O exemplo seguinte carrega um post e redireciona quando falta:
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 | Entry slug or ID |
options | { locale?: string; references?: ReferenceSelection } | Optional. Locale for slug resolution, and the reference fields to load |
O modo preview é gerido automaticamente: quando o pedido tem um token _preview válido, a consulta serve conteúdo em rascunho. Nenhum parâmetro é necessário para o estado preview.
references nomeia os campos reference a carregar, indexados por slug do campo. true pede a primeira página com o limite predefinido de 50 entradas; a forma objeto aceita limit (no máximo 100) e o cursor de uma página anterior. Campos omitidos não são lidos, e uma chamada sem a opção não executa consultas reference.
Returns
A função resolve-se num EntryResult:
interface EntryResult<T, R> {
entry: ContentEntry<T, R> | null; // null if not found
error?: Error; // Set only for actual errors, not "not found"
isPreview: boolean; // true if draft content is being served
fallbackLocale?: string; // Set when locale fallback returned another locale
cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
}
Examples
Os exemplos seguintes obtêm por slug e ID, leem o estado preview e distinguem erros de ausência:
// 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");
}
getTranslations()
Obtém as traduções disponíveis de uma entrada por slug de collection e ID de base de dados:
import { getTranslations } from "emdash";
const { translations, error } = await getTranslations("posts", post.data.id);
O resultado contém a translationGroup partilhada, um array translations e um error opcional. Cada resumo de tradução inclui id, locale, slug e status.
resolveEmDashPath()
Resolve um pathname público face aos padrões URL configurados para collections encaminháveis:
import { resolveEmDashPath } from "emdash";
const result = await resolveEmDashPath("/blog/hello-world");
if (result) {
console.log(result.collection, result.entry.data.title);
}
O resultado contém a collection correspondente, a entry e os params de rota. A função devolve null quando nenhum padrão URL configurado corresponde.
getEditMeta()
Lê os metadados de edição visual não enumeráveis anexados a um valor Portable Text:
import { getEditMeta } from "emdash";
const meta = getEditMeta(post.data.content);
Devolve { collection, id, field } para um valor anotado, ou undefined quando o valor não tem anotação.
Tipos de conteúdo
ContentEntry
As funções de consulta devolvem entradas com a seguinte forma:
interface ContentEntry<T = Record<string, unknown>, R = ReferencePages> {
id: string;
data: T;
references?: R; // One page per reference field requested; absent otherwise
edit: EditProxy; // Visual editing annotations
}
O proxy edit fornece anotações de edição visual. Expanda-o sobre elementos para ativar edição inline: {...entry.edit.title}. Fora do modo de edição, não produz saída.
O objeto data contém todos os campos de conteúdo mais campos de sistema:
id- Unique identifierslug- URL-friendly identifierstatus- “draft” | “published” | “archived”createdAt- Creation time as aDateupdatedAt- Last update time as aDatepublishedAt- Publication time as aDate, ornull; retained when content is unpublished- Plus all custom fields defined in your collection schema
ReferencePage
entry.references contém uma página por campo reference pedido a getEmDashEntry, indexada por slug do campo:
interface ReferencePage<T = Record<string, unknown>> {
entries: ContentEntry<T>[];
nextCursor?: string; // Set when the field holds more entries than the limit
}
Cada entrada é mapeada como uma entrada carregada diretamente, exceto uma exceção: bylines e termos de taxonomia não são hidratados, logo data.bylines e data.terms estão ausentes.
getEmDashReferences()
Obtém uma página de um único campo reference sem reler a entrada a que pertence. Use-o para avançar além da primeira página com o nextCursor devolvido por essa página.
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection slug of the entry that holds the field |
slugOrId | string | Entry slug or ID |
field | string | Reference field slug |
options | { limit?: number; cursor?: string; locale?: string } | Optional. Defaults to 50 entries, at most 100 |
O exemplo seguinte carrega as seguintes 20 entradas ligadas:
import { getEmDashReferences } from "emdash";
const { entries, nextCursor, error } = await getEmDashReferences(
"posts",
post.id,
"related_posts",
{ cursor, limit: 20 },
);
error só é definido para erros reais. Campo desconhecido, entrada em falta ou entrada invisível ao pedido resultam num array entries vazio.
O resultado inclui também um cacheHint que nomeia as linhas lidas pela página. Uma rota que faz cache do que renderiza deve fundi-lo no seu próprio hint, como getEmDashEntry({ references }) incorpora o da primeira página, para que escrever numa dessas entradas expire a página.
Helpers de URL
decodeSlug() and slugify()
Use decodeSlug() num parâmetro de rota dinâmica antes de o passar a uma consulta de conteúdo. Devolve undefined para parâmetro em falta; caso contrário aplica decodeURIComponent(), que lança com codificação percentual malformada:
function decodeSlug(raw: string | undefined): string | undefined;
import { decodeSlug, getEmDashEntry } from "emdash";
const slug = decodeSlug(Astro.params.slug);
const { entry } = slug ? await getEmDashEntry("posts", slug) : { entry: null };
slugify(value) converte texto num slug minúsculo separado por hífens. Use-o quando um modelo precisa construir um slug a partir de uma etiqueta; slugs de entrada armazenados já vêm do EmDash.
sanitizeHref() and isSafeHref()
Estes helpers rejeitam esquemas de ligação inseguros como javascript:. isSafeHref(value) devolve um booleano. sanitizeHref(value) devolve o URL seguro original ou "#" quando o valor está vazio ou é inseguro.
import { sanitizeHref } from "emdash";
const href = sanitizeHref(menuItem.url);
Sistema de preview
generatePreviewToken()
Gera um token de preview para conteúdo em rascunho. O exemplo seguinte cria um token que expira numa hora:
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({
contentId: "posts:01HXK5MZSN...",
secret: process.env.EMDASH_PREVIEW_SECRET!,
expiresIn: 3600, // 1 hour
});
contentId deve usar o formato collection:id. expiresIn aceita segundos ou duração terminada em s, m, h, d ou w e o predefinido é "1h". Mantenha o segredo de assinatura no servidor.
verifyPreviewToken()
Verifica um token de preview e lê o payload:
import { verifyPreviewToken } from "emdash";
const result = await verifyPreviewToken({
token,
secret: process.env.EMDASH_PREVIEW_SECRET!,
});
if (result.valid) {
const { cid, exp, iat } = result.payload;
// cid is "collection:id" format, e.g. "posts:my-draft-post"
}
Passe token ou url com o secret de assinatura. Token inválido devolve { valid: false, error }, onde error é "none", "malformed", "invalid" ou "expired".
parseContentId()
Divide o valor collection:id do payload de preview nas suas duas partes:
import { parseContentId } from "emdash";
const parsed = parseContentId(result.payload.cid);
Devolve { collection, id } e lança quando o valor não tem separador de dois pontos.
getPreviewUrl() and buildPreviewUrl()
getPreviewUrl() cria e assina um URL de preview. Aceita collection, id e secret, mais valores opcionais expiresIn, baseUrl, pathPattern e locale:
import { getPreviewUrl } from "emdash";
const previewUrl = await getPreviewUrl({
collection: "posts",
id: post.id,
secret: process.env.EMDASH_PREVIEW_SECRET!,
pathPattern: "/blog/{id}",
});
Sem baseUrl, devolve um URL relativo ao site. Use buildPreviewUrl({ path, token, baseUrl? }) quando já existe um token.
isPreviewRequest()
Verifica se um pedido inclui token de preview e depois leia-o:
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.url)) {
const token = getPreviewToken(Astro.url);
// Verify and show preview content
}
getPreviewToken() devolve o parâmetro de consulta _preview ou null quando falta. O middleware EmDash verifica pedidos preview normais e fornece estado preview a getEmDashEntry() automaticamente; estes helpers servem rotas preview personalizadas e tooling.
Conversores de conteúdo
Converte entre formatos Portable Text e 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);
Definições do site
Leia definições globais do site com getSiteSettings e getSiteSetting:
function getSiteSettings(): Promise<Partial<SiteSettings>>;
function getSiteSetting<K extends SiteSettingKey>(key: K): Promise<SiteSettings[K] | undefined>;
import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settings
const settings = await getSiteSettings();
// Get single setting
const title = await getSiteSetting("title");
As definições são só de leitura via API runtime. Use a API admin para as atualizar.
getSiteSettings() devolve um objeto parcial porque chaves não definidas são omitidas. Definições de media como logo e favicon do site resolvem-se a objetos media-reference antes da função retornar.
getSiteSettingsWithCacheHint() devolve { data, cacheHint }. Passe o hint a Astro.cache.set() quando cache de rota Astro deve invalidar-se após alteração das definições do site.
SEO
Resolva valores do painel SEO e fallbacks de conteúdo de uma página com getSeoMeta():
function getSeoMeta<T>(content: SeoContentInput<T>, options?: SeoMetaOptions): SeoMeta;
interface SeoMetaOptions {
siteTitle?: string;
siteUrl?: string;
titleSeparator?: string; // Default: " | "
path?: string;
defaultOgImage?: string;
defaultTitle?: string;
defaultDescription?: string;
}
interface SeoMeta {
title: string;
description: string | null;
ogTitle: string;
ogDescription: string | null;
ogImage: string | null;
canonical: string | null;
robots: string | null;
}
import { getSeoMeta } from "emdash";
const meta = getSeoMeta(post, {
siteTitle: "Example Blog",
siteUrl: "https://example.com",
path: `/blog/${post.data.slug}`,
});
Devolve título, descrição, valores Open Graph, URL canónica e valor robots resolvidos. getContentSeo(content) devolve objeto SEO bruto sem fallbacks de modelo.
Para conteúdo traduzido, getHreflangAlternates(collection, entryId, { siteUrl? }) devolve variantes de locale publicadas e encaminháveis como objetos { hreflang, href } e adiciona entrada x-default. Devolve array vazio quando internacionalização está desativada, entrada atual marcada noindex ou não há URL absoluta do site.
Comentários
Obtém comentários aprovados e respetiva contagem para uma entrada:
import { getCommentCount, getComments } from "emdash";
const { items: comments, total } = await getComments({
collection: "posts",
contentId: post.data.id,
threaded: true,
reactions: true,
sort: "best",
});
const count = await getCommentCount("posts", post.data.id);
threaded aninha respostas sob o comentário pai. sort predefinido é "oldest"; "best" ordena comentários de topo por reações e ativa contagens automaticamente. Consultas de comentários renderizadas no servidor devolvem no máximo 500 comentários aprovados; use REST API quando cliente precisa paginação.
Menus
Obtém menus de navegação e percorre os respetivos itens, incluindo filhos aninhados:
function getMenu(name: string, options?: { locale?: string }): Promise<Menu | null>;
function getMenus(options?: { locale?: string }): Promise<MenuSummary[]>;
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));
});
}
getMenu(name, { locale? }) segue cadeia fallback de locale configurada. getMenus({ locale? }) lista resumos de menu para locale do pedido resolvida ou configurada; sem internacionalização lista cada locale. getMenuWithCacheHint() devolve { data, cacheHint } para rotas com cache Astro.
Bylines
Obtém perfil de autor por ID ou slug, ou lista entradas creditadas a um byline:
import { getByline, getBylineBySlug, getEntriesByByline } from "emdash";
const profile = await getBylineBySlug("jane-doe", { locale: "en" });
const posts = profile
? await getEntriesByByline("posts", profile.translationGroup ?? profile.id)
: [];
getByline(id) devolve perfil ou null. Pesquisa por slug aceita locale opcional e segue cadeia fallback. Consultas de conteúdo já hidratam créditos ordenados numa entrada em entry.data.bylines; use estes helpers autónomos para páginas de autor e arquivos byline.
Taxonomies
Obtém termos de taxonomia, um termo, termos de uma entrada ou entradas por termo:
function getTaxonomyTerms(
taxonomyName: string,
options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm[]>;
function getTerm(
taxonomyName: string,
slug: string,
options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm | null>;
function getTermsForEntries(
collection: string,
entryIds: string[],
taxonomyName: string,
options?: { locale?: string },
): Promise<Map<string, TaxonomyTerm[]>>;
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");
getTaxonomyDefs({ locale? }) lista uma definição por taxonomia, e getTaxonomyDef(name, { locale? }) devolve definição ou null quando nenhuma locale define taxonomia. Ambos resolvem taxonomia em cada locale; Traduzir taxonomias e termos explica que etiqueta de locale usam. Pesquisas de termos seguem cadeia fallback configurada.
getTaxonomyTerms(name, { locale?, includeCounts? }) devolve árvore para taxonomias hierárquicas e inclui contagens de entradas visíveis por predefinição. Passe includeCounts: false quando modelo não renderiza contagens. Consultas de conteúdo hidratam termos atribuídos em data.terms de cada entrada; use getEntryTerms() quando só tem nome de collection e ID de entrada.
Para páginas de arquivo que renderizam termos junto a muitas entradas, agrupe pesquisa em vez de chamar getEntryTerms() em ciclo:
import { getAllTermsForEntries, getTermsForEntries } from "emdash";
const termsByPost = await getTermsForEntries(
"posts",
posts.map(post => post.data.id),
"category",
);
const allTermsByPost = await getAllTermsForEntries(
"posts",
posts.map(post => post.data.id),
);
getTermsForEntries() devolve mapa de ID de entrada para termos da taxonomia pedida. getAllTermsForEntries() devolve mapa de ID para termos agrupados por nome de taxonomia. getTaxonomyTermsWithCacheHint() devolve { data, cacheHint } para rota com cache Astro.
Áreas de widgets
Obtém áreas de widgets e widgets que contêm:
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);
});
}
getWidgetAreaWithCacheHint(name) devolve { data, cacheHint } para rotas com cache Astro. Áreas e widgets ordenam-se pela ordem configurada.
Sections
Obtém secções e filtra-as:
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?) devolve { items: Section[]; nextCursor?: string }. Opções: source ("theme" | "user" | "import"), search, limit (predefinido 50, máx. 100) e cursor.
Pesquisa
Executa pesquisa global em collections. Resultados incluem excertos destacados:
function search(query: string, options?: SearchOptions): Promise<SearchResponse>;
interface SearchOptions {
collections?: string[]; // Default: every searchable collection
status?: string; // Default: "published"
locale?: string; // Default: all locales
limit?: number; // Default: 20
cursor?: string;
scope?: "all" | "title"; // Default: "all"
}
interface SearchResponse {
items: SearchResult[];
nextCursor?: string;
}
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);
});
// Paginate: pass the previous nextCursor back as `cursor` to get the next page.
// nextCursor is undefined once there are no more results.
if (results.nextCursor) {
const next = await search("hello world", {
collections: ["posts", "pages"],
limit: 20,
cursor: results.nextCursor,
});
}
Com scope: "title", consulta corresponde só ao campo title de cada collection em vez do texto indexado completo — útil para seletores e autocomplete onde correspondências no corpo surpreenderiam. Collections cujo title não está indexado para pesquisa não devolvem resultados neste scope.
Tratamento de erros
Consultas de conteúdo devolvem erros operacionais no resultado em vez de lançar. Entrada em falta não é erro operacional: entry é null e error permanece undefined.
const { entry, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Content could not be loaded", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
Helpers sem envelope de resultado podem lançar quando entrada é inválida ou operação de base de dados falha. Trate erros no limite da rota quando a página pode fornecer fallback útil. Classes de erro repository e handler de integrações servidor de nível inferior ficam fora desta API de modelos de site.