EmDash proporciona funciones de consulta para recuperar contenido en tus páginas y componentes de Astro. Estas funciones siguen el patrón de live content collections de Astro, devolviendo resultados estructurados con manejo de errores.
Funciones de consulta
EmDash exporta dos funciones de consulta principales:
| Función | Propósito | Retorna |
|---|---|---|
getEmDashCollection | Recuperar todas las entradas de un tipo de contenido | { entries, error } |
getEmDashEntry | Recuperar una entrada por ID o slug | { entry, error, isPreview } |
Impórtalas desde emdash:
import { getEmDashCollection, getEmDashEntry } from "emdash";
Obtener todas las entradas
Usa getEmDashCollection para recuperar todas las entradas de un tipo de contenido:
---
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
console.error("Error al cargar posts:", error);
}
---
<ul>
{posts.map((post) => (
<li>{post.data.title}</li>
))}
</ul>
Filtrar por locale
Cuando i18n está habilitado, filtra por locale para recuperar contenido en un idioma específico:
// Posts en francés
const { entries: frenchPosts } = await getEmDashCollection("posts", {
locale: "fr",
status: "published",
});
// Usar el locale de la solicitud actual
const { entries: localizedPosts } = await getEmDashCollection("posts", {
locale: Astro.currentLocale,
status: "published",
});
Para entradas individuales, pasa locale como tercer argumento:
const { entry: post } = await getEmDashEntry("posts", "my-post", {
locale: Astro.currentLocale,
});
Cuando se omite locale, se usa por defecto el locale actual de la solicitud. Si no existe traducción para el locale solicitado, se sigue la cadena de respaldo.
Filtrar por estado
Recuperar solo contenido publicado o en borrador:
// Solo posts publicados
const { entries: published } = await getEmDashCollection("posts", {
status: "published",
});
// Solo borradores
const { entries: drafts } = await getEmDashCollection("posts", {
status: "draft",
});
Limitar resultados
Restringir el número de entradas devueltas:
// Obtener los 5 posts más recientes
const { entries: recentPosts } = await getEmDashCollection("posts", {
status: "published",
limit: 5,
});
Filtrar por taxonomía
Filtrar entradas por categoría, etiqueta o términos de taxonomía personalizados:
// Posts en la categoría "news"
const { entries: newsPosts } = await getEmDashCollection("posts", {
status: "published",
where: { category: "news" },
});
// Posts con la etiqueta "javascript"
const { entries: jsPosts } = await getEmDashCollection("posts", {
status: "published",
where: { tag: "javascript" },
});
// Posts que coincidan con cualquiera de múltiples términos
const { entries: featuredNews } = await getEmDashCollection("posts", {
status: "published",
where: { category: ["news", "featured"] },
});
El filtro where usa lógica OR cuando se proporcionan múltiples valores para una sola taxonomía.
Manejo de errores
Siempre verifica los errores cuando la fiabilidad importa:
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) {
console.error("Error al cargar posts:", error);
return new Response("Error del servidor", { status: 500 });
}
Obtener una entrada individual
Usa getEmDashEntry para recuperar una entrada por su ID o slug:
---
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";
const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Error del servidor", { status: 500 });
}
if (!post) {
return Astro.redirect("/404");
}
---
<article>
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
Tipo de retorno de entrada
getEmDashEntry devuelve un objeto resultado:
interface EntryResult<T> {
entry: ContentEntry<T> | null;
error?: Error;
isPreview: boolean;
}
interface ContentEntry<T> {
id: string;
data: T;
edit: EditProxy;
}
El objeto data dentro de entry contiene todos los campos definidos para el tipo de contenido. El proxy edit proporciona anotaciones de edición visual (ver abajo).
Renderizar datos del panel SEO
Para colecciones con supports: ["seo"], los editores pueden configurar un título SEO, meta descripción, imagen OG, URL canónica y un toggle “ocultar de motores de búsqueda” (noindex) en el panel SEO del admin. Esos datos se entregan como entry.data.seo — pero solo llegan a la página renderizada si tu plantilla los emite. Usa getSeoMeta para resolver los campos del panel (con respaldos sensatos a data.title / data.excerpt) en meta tags listos para renderizar:
---
import { getEmDashEntry, getSeoMeta } from "emdash";
const { entry, error } = await getEmDashEntry("posts", Astro.params.slug);
if (error) {
return new Response("Error del servidor", { status: 500 });
}
if (!entry) return Astro.redirect("/404");
const seo = getSeoMeta(entry, {
siteTitle: "Mi Sitio",
siteUrl: "https://example.com",
path: Astro.url.pathname,
});
---
<head>
<title>{seo.title}</title>
{seo.description && <meta name="description" content={seo.description} />}
{seo.ogImage && <meta property="og:image" content={seo.ogImage} />}
{seo.canonical && <link rel="canonical" href={seo.canonical} />}
{seo.robots && <meta name="robots" content={seo.robots} />}
</head>
Modo vista previa
EmDash maneja la vista previa automáticamente a través de middleware. Cuando una URL contiene un token _preview válido, el middleware lo verifica y configura el contexto de la solicitud. Tus funciones de consulta sirven contenido en borrador sin parámetros especiales:
---
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Error del servidor", { status: 500 });
}
if (!entry) {
return Astro.redirect("/404");
}
---
{isPreview && (
<div class="preview-banner">
Viendo vista previa. Este contenido no está publicado.
</div>
)}
<article>
<h1>{entry.data.title}</h1>
<PortableText value={entry.data.content} />
</article>
Edición visual
Cada entrada devuelta por las funciones de consulta incluye un proxy edit para anotar tus plantillas. Espárcelo en elementos para habilitar la edición inline para editores autenticados:
<article {...entry.edit}>
<h1 {...entry.edit.title}>{entry.data.title}</h1>
<div {...entry.edit.content}>
<PortableText value={entry.data.content} />
</div>
</article>
En modo de edición, {...entry.edit.title} produce un atributo data-emdash-ref que la barra de herramientas de edición visual usa para habilitar la edición inline. En producción, los spreads del proxy no producen salida.
Estilar bloques de código inline
Los bloques de código inline siguen la apariencia clara u oscura del sistema por defecto. Si tu sitio tiene su propio interruptor de tema, establece estas propiedades en sus selectores de tema claro y oscuro:
| Propiedad | Propósito |
|---|---|
--emdash-inline-code-background | Fondo del bloque de código |
--emdash-inline-code-foreground | Texto de código simple |
--emdash-inline-code-muted | Comentarios y texto citado |
--emdash-inline-code-keyword | Palabras clave, literales, selectores y texto eliminado |
--emdash-inline-code-string | Strings, atributos, símbolos y texto añadido |
--emdash-inline-code-number | Números y metadatos |
--emdash-inline-code-title | Títulos, nombres, tipos y built-ins |
--emdash-inline-code-border | Borde del selector de idioma |
--emdash-inline-code-control-background | Fondo del selector de idioma |
--emdash-inline-code-control-foreground | Texto e iconos del selector de idioma |
--emdash-inline-code-focus | Indicador de foco del teclado |
El siguiente ejemplo estila bloques de código inline para un sitio que aplica .dark al elemento <html>:
:root {
--emdash-inline-code-background: #f7f7f5;
--emdash-inline-code-foreground: #24292f;
--emdash-inline-code-muted: #57606a;
--emdash-inline-code-keyword: #b8172a;
--emdash-inline-code-string: #0a3069;
--emdash-inline-code-number: #0550ae;
--emdash-inline-code-title: #7545c7;
--emdash-inline-code-border: #7d8590;
--emdash-inline-code-control-background: #fff;
--emdash-inline-code-control-foreground: #24292f;
--emdash-inline-code-focus: #0550ae;
}
:root.dark {
--emdash-inline-code-background: #202020;
--emdash-inline-code-foreground: #f0f3f6;
--emdash-inline-code-muted: #c9d1d9;
--emdash-inline-code-keyword: #ffc1bb;
--emdash-inline-code-string: #b9ddff;
--emdash-inline-code-number: #a8d5ff;
--emdash-inline-code-title: #e5ccff;
--emdash-inline-code-border: #6e7681;
--emdash-inline-code-control-background: #161b22;
--emdash-inline-code-control-foreground: #f0f3f6;
--emdash-inline-code-focus: #a8d5ff;
}
Si cambias el fondo del bloque de código, también sobreescribe cada color de texto para que la sintaxis siga siendo legible. Estas propiedades se aplican solo mientras la edición inline está activa. Estila los bloques de código publicados con el CSS de tu sitio.
Ordenar resultados
getEmDashCollection no garantiza el orden de clasificación. Ordena los resultados en tu plantilla:
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
const sorted = posts.sort(
(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0),
);
Patrones de ordenamiento comunes
// Alfabético por título
posts.sort((a, b) => a.data.title.localeCompare(b.data.title));
// Por campo de orden personalizado
posts.sort((a, b) => (a.data.order ?? 0) - (b.data.order ?? 0));
// Orden aleatorio
posts.sort(() => Math.random() - 0.5);
Tipos TypeScript
Genera tipos TypeScript para tus colecciones:
npx emdash types
Esto crea .emdash/types.ts con interfaces para cada colección. Úsalos para seguridad de tipos:
import { getEmDashCollection, getEmDashEntry } from "emdash";
import type { Post } from "../.emdash/types";
const { entries: posts } = await getEmDashCollection<Post>("posts");
const { entry: post } = await getEmDashEntry<Post>("posts", "my-post");
Renderizado estático vs. servidor
El contenido de EmDash funciona tanto con páginas estáticas como renderizadas en servidor.
Estático (Pre-renderizado)
Para páginas estáticas, usa getStaticPaths para generar rutas en tiempo de compilación:
---
import { getEmDashCollection, getEmDashEntry } from "emdash";
export async function getStaticPaths() {
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
return posts.map((post) => ({
params: { slug: post.data.slug },
}));
}
const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
---
Renderizado en servidor
Para páginas renderizadas en servidor, consulta el contenido directamente:
---
export const prerender = false;
import { getEmDashEntry } from "emdash";
const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);
if (error) {
return new Response("Error del servidor", { status: 500 });
}
if (!post) {
return new Response(null, { status: 404 });
}
---
Consideraciones de rendimiento
Caché
EmDash usa las live content collections de Astro, que manejan el caché automáticamente. Para páginas renderizadas en servidor, considera agregar encabezados de caché HTTP:
---
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
Astro.response.headers.set("Cache-Control", "public, max-age=300");
---
Evitar consultas redundantes
Consulta una vez y pasa los datos a los componentes:
---
import { getEmDashCollection } from "emdash";
import PostList from "../components/PostList.astro";
import Sidebar from "../components/Sidebar.astro";
const { entries: posts } = await getEmDashCollection("posts", {
status: "published",
});
const featured = posts.filter((p) => p.data.featured);
const recent = posts.slice(0, 5);
---
<PostList posts={featured} />
<Sidebar posts={recent} />
Próximos pasos
- Crear un blog - Construir un blog completo
- Taxonomías - Filtrar por categorías y etiquetas
- Trabajar con contenido - Operaciones CRUD del admin
- Internacionalización - Contenido multilingüe y traducciones