La plantilla de blog de EmDash ofrece un sitio Astro funcional con publicaciones, páginas, autores, categorías, etiquetas, búsqueda, comentarios, widgets y un feed RSS. Este tutorial crea la versión de Cloudflare, publica una publicación y sigue esa publicación a través del código de la plantilla.
Requisitos previos
Antes de empezar, instala Node.js 22.12 o posterior y pnpm.
Solo necesitas una cuenta de Cloudflare cuando despliegues el sitio. Durante el desarrollo local, la plantilla ejecuta en tu ordenador versiones locales de la base de datos y del almacenamiento de archivos.
Crear la estructura del blog
El siguiente comando crea my-blog a partir de la plantilla de blog de Cloudflare e instala sus dependencias con pnpm:
npm create emdash@latest my-blog -- --template cloudflare:blog --pm pnpm --yes
El generador de estructura también crea un archivo .env local con una EMDASH_ENCRYPTION_KEY. El .gitignore generado excluye .env del control de versiones. Si el comando informa de que la instalación de dependencias ha fallado, entra en el directorio del proyecto y ejecuta pnpm install antes de continuar.
Inicia el servidor de desarrollo local:
cd my-blog
pnpm dev
Abre la URL local que aparece en el terminal y luego abre /_emdash/admin. Completa la pantalla de configuración si es la primera ejecución. Los datos seed de la plantilla crean el modelo de contenido y el contenido de ejemplo durante la configuración.
Entender el modelo de contenido
La plantilla define dos colecciones en seed/seed.json:
postshabilita borradores, revisiones, búsqueda y SEO mediantesupports, y habilita los comentarios por separado concommentsEnabled: true;pagesadmite borradores, revisiones y búsqueda.
Cada publicación tiene estos campos personalizados:
| Campo | Finalidad |
|---|---|
title | Título obligatorio de la publicación |
featured_image | Imagen opcional que se muestra con la publicación |
content | Cuerpo en Portable Text |
excerpt | Texto breve usado en las listas de publicaciones y como valor alternativo de los metadatos |
EmDash añade campos del sistema, como el ID de contenido estable, el slug, el estado, las fechas de creación y actualización y la fecha de publicación. La plantilla también define las taxonomías category y tag para las publicaciones, además de bylines que pueden acreditar a uno o varios autores.
El servidor de desarrollo genera emdash-env.d.ts a partir de este esquema. Como resultado, getEmDashCollection("posts") devuelve entradas cuya propiedad data está tipada como Post.
Publicar la primera publicación
-
En la barra lateral del administrador, selecciona Posts y luego Add New.
-
Introduce un título. EmDash sugiere un slug a partir del título; edítalo si la URL pública necesita otro valor.
-
Añade un extracto y escribe el cuerpo en el editor Content.
-
Selecciona una imagen destacada de la biblioteca de medios o sube una. Añade un texto alternativo que describa la función de la imagen en la publicación.
-
Asigna un byline, una categoría y las etiquetas que correspondan en el panel de ajustes.
-
Selecciona Save. La entrada pasa a ser un borrador y el editor abre su URL permanente.
-
Selecciona Preview y comprueba la página de la publicación. Vuelve al editor y selecciona Publish cuando el borrador esté listo.
Abre /posts/your-post-slug en el sitio local. La publicación también aparece en la página de inicio y en el archivo de publicaciones. Si no aparece, confirma que el editor muestra Published en lugar de Draft o Scheduled.
Tras la publicación, las ediciones se guardan automáticamente en un nuevo borrador mientras la publicación actual sigue en vivo. Selecciona Publish changes cuando el borrador revisado deba reemplazarla. La guía de creación de contenido explica las vistas previas, la programación, las revisiones y los bloqueos de edición.
Seguir la consulta de colección
La página de inicio y el archivo de publicaciones llaman a getEmDashCollection() durante el renderizado en el servidor. La plantilla ordena las publicaciones en la base de datos por el campo almacenado published_at:
---
import { getEmDashCollection, getTermsForEntries } from "emdash";
const { entries: posts, cacheHint } = await getEmDashCollection("posts", {
orderBy: { published_at: "desc" },
});
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
const tagsByEntry = await getTermsForEntries(
"posts",
posts.map((post) => post.data.id),
"tag",
);
---
La consulta de colección devuelve las entradas publicadas de forma predeterminada. Usa published_at, el nombre del campo en la base de datos, para ordenar. La propiedad publishedAt devuelta es un Date de JavaScript para el renderizado.
El helper de taxonomías recibe post.data.id porque las asignaciones de taxonomía pertenecen al ID de contenido estable. Los enlaces usan en cambio post.id, porque es el slug orientado a la URL que produce el cargador de contenido:
<a href={`/posts/${post.id}`}>
<h2>{post.data.title}</h2>
{post.data.excerpt && <p>{post.data.excerpt}</p>}
</a>
La plantilla actual agrupa las búsquedas de etiquetas con getTermsForEntries() en lugar de consultar una vez por cada publicación. Los bylines ya están incluidos en post.data.bylines por la consulta de colección.
Seguir la consulta de publicación
La ruta dinámica de la publicación lee el slug de la URL y llama a getEmDashEntry(). El siguiente extracto muestra la ruta esencial de consulta y renderizado, mientras que la plantilla completa también se encarga de SEO, bylines, comentarios, publicaciones relacionadas y widgets:
---
import { decodeSlug, getEmDashEntry } from "emdash";
import { Image, PortableText } from "emdash/ui";
const slug = decodeSlug(Astro.params.slug);
if (!slug) return Astro.rewrite("/404");
const { entry: post, error, cacheHint } = await getEmDashEntry("posts", slug);
if (error) return new Response("Unable to load post", { status: 500 });
if (!post) return Astro.rewrite("/404");
if (Astro.cache?.enabled) Astro.cache.set(cacheHint);
---
<article>
{post.data.featured_image && <Image image={post.data.featured_image} priority />}
<h1>{post.data.title}</h1>
<PortableText value={post.data.content} />
</article>
Image lee el valor de medio que eligió el editor y genera una salida adaptable. PortableText convierte los datos de bloques almacenados en encabezados, párrafos, enlaces, imágenes, bloques de código y los demás tipos de bloque admitidos.
Ambas plantillas de blog establecen output: "server" en astro.config.mjs. Estas consultas se ejecutan cuando se renderiza una solicitud, por lo que el contenido publicado no depende de una lista estática de rutas creada durante la compilación.
Usar categorías y etiquetas
La plantilla incluye rutas de archivo para cada categoría y etiqueta. La ruta de una categoría primero resuelve el slug del término y luego filtra las publicaciones por esa taxonomía:
---
import { decodeSlug, getEmDashCollection, getTerm } from "emdash";
const slug = decodeSlug(Astro.params.slug);
const category = slug
? await getTerm("category", slug, { includeCounts: false })
: null;
if (!category) return Astro.rewrite("/404");
const { entries: posts, error } = await getEmDashCollection("posts", {
where: { category: category.slug },
orderBy: { published_at: "desc" },
});
if (error) return new Response("Unable to load posts", { status: 500 });
---
La ruta tag usa el mismo patrón con getTerm("tag", slug) y where: { tag: term.slug }. Los editores gestionan los términos y las asignaciones en el administrador; la guía de taxonomías cubre las categorías jerárquicas, las etiquetas planas y las taxonomías personalizadas.
getEmDashEntry() incluye los términos asignados a la publicación, de modo que la ruta de detalle puede renderizarlos sin otra consulta:
---
const categories = post.data.terms?.category ?? [];
const tags = post.data.terms?.tag ?? [];
---
{categories.map((category) => (
<a href={`/category/${category.slug}`}>{category.label}</a>
))}
{tags.map((tag) => (
<a href={`/tag/${tag.slug}`}>{tag.label}</a>
))}
Agregar paginación al archivo
El archivo de publicaciones de la plantilla renderiza todas las publicaciones publicadas. Cuando el archivo crezca, añade un limit y usa paginación por desplazamiento para rutas numeradas como /posts/page/2, o paginación por cursor para un enlace Older posts. Mantén orderBy: { published_at: "desc" } sin cambios entre páginas para que las entradas no cambien de orden de forma inesperada.
Los ejemplos de paginación muestran ambos enfoques y explican cuándo elegir cada uno.
Verificar el feed RSS
La plantilla ya sirve /rss.xml. Su endpoint lee las 20 publicaciones más recientes con getEmDashCollection(), da formato a cada fecha de publicación y escapa el título y el extracto antes de insertarlos en el XML. También lee el título y el eslogan del sitio desde los ajustes de EmDash.
Después de publicar la publicación de prueba, abre /rss.xml y busca su título. Si el sitio va a usar una URL absoluta de producción en los feeds, establece la opción site de Astro antes del despliegue; durante el desarrollo local, el endpoint recurre al origen de la solicitud actual.
En este punto, el blog tiene un flujo de creación, páginas de publicación en tiempo de ejecución, archivos de taxonomías, renderizado de medios y un feed. Continúa con Consultar contenido para ver filtros y paginación, o con Biblioteca de medios para la edición de recursos y el seguimiento de su uso. Para redactar o editar publicaciones con un asistente de IA, sigue Herramientas de IA.