Esta página descreve a API pública que páginas, layouts e componentes do Astro usam para ler e apresentar um site EmDash. Ela não lista todas as exportações da raiz do pacote emdash: repositórios de banco de dados, handlers de API, utilitários de migração, APIs para criar plugins e outros elementos internos do servidor têm referências próprias ou são destinados ao código de integração com o framework.
O contrato de API de modelos de site desta referência lista todas as funções que os modelos de site mantidos pelo EmDash importam de emdash. Importações apenas de tipo, como MediaValue, pertencem à documentação do modelo de dados correspondente. As funções relacionadas são documentadas nas mesmas seções quando ajudam quem cria um site, mas a página não cobre exportações da raiz sem relação com o tema.
Contrato de API de modelos de site
Os modelos mantidos chamam estes helpers de runtime:
| Função | Usada para |
|---|---|
decodeSlug | Decodificar um parâmetro de rota dinâmica antes da consulta |
getEmDashCollection | Ler e filtrar entradas de uma collection |
getEmDashEntry | Ler uma entrada por ID ou slug |
getMenuWithCacheHint | Renderizar um menu de navegação com invalidação de cache |
getSeoMeta | Resolver os valores do painel de SEO de uma entrada e os fallbacks do modelo |
getSiteSettings | Ler a identidade pública do site e outras configurações globais |
getSiteSettingsWithCacheHint | Ler as configuraçõ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 de URL inseguros antes de renderizar links armazenados |
search | Pesquisar conteúdo publicado em várias collections |
Consultas de conteúdo
As funções de consulta do EmDash seguem o padrão das live content collections do Astro e retornam { entries, error } ou { entry, error } para tratar erros com elegância.
getEmDashCollection()
Busca todas as entradas de uma collection. O exemplo a seguir carrega todos os posts e verifica se houve erro:
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
console.error("Failed to load posts:", error);
}
Parâmetros
| Parâmetro | Tipo | Descrição |
|---|---|---|
collection | string | Slug da collection |
options | CollectionFilter | Opções de filtro opcionais |
Opções
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 de where podem nomear campos de conteúdo, taxonomias ou byline; objetos de intervalo estão disponíveis para comparações ordenadas.
Retorno
A função é resolvida em um 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)
}
Exemplos
Os exemplos a seguir filtram por status e taxonomia, limitam os 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()
Busca uma única entrada por slug ou ID. O exemplo a seguir carrega um post e renderiza a página 404 do site quando ele não existe:
import { getEmDashEntry } from "emdash";
const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");
if (!post) {
return Astro.rewrite("/404");
}
Parâmetros
| Parâmetro | Tipo | Descrição |
|---|---|---|
collection | string | Slug da collection |
slugOrId | string | Slug ou ID da entrada |
options | { locale?: string; references?: ReferenceSelection } | Opcional. Locale para a resolução do slug e os campos de referência a carregar |
O modo de pré-visualização é tratado automaticamente: quando a requisição tem um token _preview válido, a consulta serve o conteúdo em rascunho. O estado de pré-visualização não exige nenhum parâmetro.
references nomeia os campos de referência a carregar, indexados pelo slug do campo. true solicita a primeira página com o limite padrão de 50 entradas; a forma de objeto aceita um limit (no máximo 100) e o cursor de uma página anterior. Os campos omitidos não são lidos, e uma chamada que omite a opção não executa nenhuma consulta de referências.
Retorno
A função é resolvida em um 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
}
Exemplos
Os exemplos a seguir buscam entradas por slug e ID, leem o estado de pré-visualização e distinguem erros de «não encontrado»:
// 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.rewrite("/404");
}
getTranslations()
Busca as traduções disponíveis de uma entrada a partir do slug da collection e do ID do banco de dados:
import { getTranslations } from "emdash";
const { translations, error } = await getTranslations("posts", post.data.id);
O resultado contém o translationGroup compartilhado, um array translations e um error opcional. Cada resumo de tradução inclui id, locale, slug e status.
resolveEmDashPath()
Resolve um caminho público com base nos padrões de URL configurados para as collections roteá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 da rota. A função retorna null quando nenhum padrão de 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);
Retorna { 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 retornam entradas no seguinte formato:
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. Espalhe-o em elementos para habilitar a edição em linha: {...entry.edit.title}. Fora do modo de edição, ele não produz nenhuma saída.
O objeto data contém todos os campos de conteúdo mais os campos do sistema:
id- Identificador únicoslug- Identificador amigável para URLsstatus- “draft” | “published” | “archived”createdAt- Data de criação comoDateupdatedAt- Data da última atualização comoDatepublishedAt- Data de publicação comoDate, ounull; é mantida quando o conteúdo é despublicado- Além de todos os campos personalizados definidos no esquema da sua collection
ReferencePage
entry.references contém uma página para cada campo de referência solicitado a getEmDashEntry, indexada pelo 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 do mesmo modo que uma entrada carregada diretamente, com uma exceção: bylines e termos de taxonomia não são hidratados, portanto data.bylines e data.terms não existem.
getEmDashReferences()
Busca uma página de um único campo de referência, sem reler a entrada à qual ele pertence. Use-a para avançar além da primeira página com o nextCursor que essa página retornou.
| Parâmetro | Tipo | Descrição |
|---|---|---|
collection | string | Slug da collection da entrada que contém o campo |
slugOrId | string | Slug ou ID da entrada |
field | string | Slug do campo de referência |
options | { limit?: number; cursor?: string; locale?: string } | Opcional. Padrão de 50 entradas, no máximo 100 |
O exemplo a seguir carrega as próximas 20 entradas vinculadas:
import { getEmDashReferences } from "emdash";
const { entries, nextCursor, error } = await getEmDashReferences(
"posts",
post.id,
"related_posts",
{ cursor, limit: 20 },
);
error só é definido em erros reais. Um campo desconhecido, uma entrada inexistente ou uma entrada que a requisição não pode ver se resolvem todos em um array entries vazio.
O resultado também traz um cacheHint que nomeia as linhas lidas pela página. Uma rota que armazena em cache o que renderiza deve mesclá-lo à sua própria dica, do mesmo modo que getEmDashEntry({ references }) incorpora a da primeira página, para que uma gravação em uma dessas entradas faça a página expirar.
Helpers de URL
decodeSlug() e slugify()
Use decodeSlug() em um parâmetro de rota dinâmica antes de passá-lo a uma consulta de conteúdo. Ela retorna undefined quando o parâmetro está ausente e, caso contrário, aplica decodeURIComponent(), que lança uma exceção se a codificação percentual estiver 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 em um slug em minúsculas, separado por hífens. Use-a quando um modelo precisar construir um slug a partir de um rótulo; os slugs de entradas armazenadas já vêm do EmDash.
sanitizeHref() e isSafeHref()
Esses helpers aceitam http:, https:, mailto:, tel:, caminhos relativos ao site e fragmentos. Eles rejeitam esquemas capazes de executar scripts, URLs relativas ao protocolo, formas iniciadas por barra invertida que os navegadores resolvem para outro site e caracteres de controle. isSafeHref(value) retorna um booleano. sanitizeHref(value) retorna a URL segura 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 pré-visualização para conteúdo em rascunho. O exemplo a seguir cria um token que expira em uma 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 uma duração terminada em s, m, h, d ou w, e o padrão é "1h". Mantenha o segredo de assinatura no servidor.
verifyPreviewToken()
Verifica um token de pré-visualização e lê seu 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 junto com o secret de assinatura. Um token inválido retorna { valid: false, error }, em que error é "none", "malformed", "invalid" ou "expired".
parseContentId()
Divide em duas partes o valor collection:id do payload de uma pré-visualização:
import { parseContentId } from "emdash";
const parsed = parseContentId(result.payload.cid);
Retorna { collection, id } e lança uma exceção quando o valor não tem o separador de dois-pontos.
getPreviewUrl() e buildPreviewUrl()
getPreviewUrl() cria e assina uma URL de pré-visualização. Ela aceita collection, id e secret, além dos 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, retorna uma URL relativa ao site. Use buildPreviewUrl({ path, token, baseUrl? }) quando já existir um token.
isPreviewRequest()
Verifica se uma requisição inclui um token de pré-visualização e, em seguida, o lê:
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.url)) {
const token = getPreviewToken(Astro.url);
// Verify and show preview content
}
getPreviewToken() retorna o parâmetro de consulta _preview ou null quando ele está ausente. O middleware do EmDash verifica as requisições de pré-visualização normais e fornece o estado de pré-visualização a getEmDashEntry() automaticamente; esses helpers servem para rotas de pré-visualização personalizadas e ferramentas.
Conversores de conteúdo
Converta entre os 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 as configurações de todo o 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 configurações são somente leitura na API de runtime. Use a API de administração para atualizá-las.
getSiteSettings() retorna um objeto parcial porque as chaves não definidas são omitidas. As configurações de mídia, como o logotipo e o favicon do site, são resolvidas em objetos de referência de mídia antes de a função retornar.
getSiteSettingsWithCacheHint() retorna { data, cacheHint }. Passe a dica a Astro.cache.set() quando um cache de rotas do Astro precisar ser invalidado após uma alteração nas configurações do site.
SEO
Resolva os valores do painel de SEO e os fallbacks do 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}`,
});
Retorna o título resolvido, a descrição, os valores do Open Graph, a URL canônica e o valor de robots. getContentSeo(content) retorna o objeto de SEO bruto, sem aplicar os fallbacks do modelo.
Para conteúdo traduzido, getHreflangAlternates(collection, entryId, { siteUrl? }) retorna as variantes de locale publicadas e roteáveis como objetos { hreflang, href } e adiciona uma entrada x-default. Retorna um array vazio quando a internacionalização está desabilitada, a entrada atual está marcada como noindex ou não há uma URL absoluta do site disponível.
Comentários
Busque os comentários aprovados de uma entrada e a contagem deles:
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 as respostas abaixo do comentário pai. O padrão de sort é "oldest"; "best" classifica os comentários de nível superior por reações e habilita automaticamente a contagem de reações. As consultas de comentários renderizadas no servidor retornam no máximo 500 comentários aprovados; use a API REST quando um cliente precisar de paginação.
Menus
Busque menus de navegação e percorra seus itens, incluindo os 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 a cadeia de fallback de locales configurada. getMenus({ locale? }) lista os resumos de menus para o locale da requisição resolvido ou o configurado; sem internacionalização, lista todos os locales. getMenuWithCacheHint() retorna { data, cacheHint } para rotas que usam o cache do Astro.
Bylines
Busque o perfil de um autor por ID ou slug, ou liste as 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) retorna um perfil ou null. A busca por slug aceita um locale opcional e segue a cadeia de fallback de locales. As consultas de conteúdo já hidratam os créditos ordenados de uma entrada em entry.data.bylines; use esses helpers independentes para páginas de autor e arquivos de bylines.
Taxonomias
Busque termos de taxonomia, um único termo, os 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? }) retorna uma definição, ou null quando nenhum locale define a taxonomia. Ambas resolvem uma taxonomia em todos os locales; Traduzir taxonomias e termos explica qual rótulo de locale elas usam. As buscas de termos seguem a cadeia de fallback configurada.
getTaxonomyTerms(name, { locale?, includeCounts? }) retorna uma árvore para taxonomias hierárquicas e inclui, por padrão, a contagem de entradas visíveis. Passe includeCounts: false quando o modelo não renderizar as contagens. As consultas de conteúdo também hidratam os termos atribuídos no data.terms de cada entrada; use getEntryTerms() quando você tiver apenas o nome de uma collection e o ID de uma entrada.
Em páginas de arquivo que renderizam termos ao lado de muitas entradas, agrupe a busca em lote em vez de chamar getEntryTerms() em um laço:
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() retorna um mapa do ID da entrada para os termos da taxonomia solicitada. getAllTermsForEntries() retorna um mapa do ID da entrada para os termos agrupados por nome de taxonomia. getTaxonomyTermsWithCacheHint() retorna { data, cacheHint } para uma rota com cache do Astro.
Áreas de widgets
Busque áreas de widgets e os widgets que elas 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) retorna { data, cacheHint } para rotas que usam o cache do Astro. As áreas de widgets e seus widgets são ordenados pela ordem de classificação configurada.
Sections
Busque sections e filtre-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?) retorna { items: Section[]; nextCursor?: string }. As opções são source ("theme" | "user" | "import"), search, limit (padrão 50, máximo 100) e cursor.
Pesquisa
Execute uma pesquisa global em várias collections. Os resultados incluem trechos 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", a consulta corresponde apenas ao campo de título de cada collection, em vez de todo o texto indexado — útil para seletores e preenchimento automático, em que correspondências no corpo do texto surpreenderiam. As collections cujo campo de título não está indexado para pesquisa não retornam resultados nesse escopo.
Tratamento de erros
As consultas de conteúdo retornam erros operacionais no próprio resultado, em vez de lançá-los. Uma entrada ausente não é um erro operacional: entry é null e error permanece indefinido.
const { entry, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Content could not be loaded", { status: 500 });
}
if (!entry) {
return Astro.rewrite("/404");
}
Helpers sem um envelope de resultado podem lançar exceções quando a entrada é inválida ou uma operação de banco de dados falha. Trate esses erros no limite da rota quando a página puder oferecer um fallback útil. As classes de erro de repositórios e handlers usadas por integrações de servidor de nível mais baixo estão fora desta API de modelos de site.