Crear un Blog

En esta página

La plantilla de blog de EmDash proporciona 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 rastrea esa publicación a través del código de la plantilla.

Requisitos previos

Instala Node.js 22.12 o posterior y pnpm antes de comenzar.

Solo necesitas una cuenta de Cloudflare cuando despliegues el sitio. Durante el desarrollo local, la plantilla ejecuta versiones locales de la base de datos y el almacenamiento de archivos en tu computadora.

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 scaffolder también crea un archivo .env local con un EMDASH_ENCRYPTION_KEY. El .gitignore generado excluye .env del control de versiones. Si el comando informa que la instalación de dependencias falló, entra al 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 impresa en la terminal, luego abre /_emdash/admin. Completa la pantalla de configuración si es la primera ejecución. Los datos semilla 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:

  • posts habilita borradores, revisiones, búsqueda y SEO a través de supports, y habilita comentarios por separado con commentsEnabled: true;
  • pages soporta borradores, revisiones y búsqueda.

Cada publicación tiene estos campos personalizados:

CampoPropósito
titleTítulo obligatorio de la publicación
featured_imageImagen opcional mostrada con la publicación
contentCuerpo en Portable Text
excerptTexto corto usado en listas de publicaciones y fallbacks de metadatos

EmDash agrega campos del sistema como el ID estable de contenido, slug, estado, tiempos de creación y actualización, y tiempo de publicación. La plantilla también define taxonomías category y tag para publicaciones, más líneas de autoría que pueden acreditar uno o más 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

  1. En la barra lateral del admin, selecciona Posts, luego Add New.

  2. Ingresa un título. EmDash sugiere un slug a partir del título; edítalo si la URL pública necesita un valor diferente.

  3. Agrega un extracto y escribe el cuerpo en el editor de Content.

  4. Selecciona una imagen destacada de la Biblioteca de Medios o sube una. Agrega texto alternativo que describa el propósito de la imagen en la publicación.

  5. Asigna una línea de autoría, categoría y etiquetas relevantes en el panel de configuración.

  6. Selecciona Save. La entrada se convierte en borrador y el editor abre su URL permanente.

  7. Selecciona Preview y revisa la página de la publicación. Regresa 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 el archivo de publicaciones. Si no aparece, confirma que el editor muestra Published, no Draft o Scheduled.

Después de la publicación, las ediciones se guardan automáticamente en un nuevo borrador mientras la publicación actual permanece activa. Selecciona Publish changes cuando el borrador revisado deba reemplazarla. La guía de autoría de contenido explica vistas previas, programación, revisiones y 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 del 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 entradas publicadas por defecto. Usa published_at, el nombre del campo de la base de datos, para ordenar. La propiedad publishedAt devuelta es un Date de JavaScript para renderizado.

El helper de taxonomía recibe post.data.id porque las asignaciones de taxonomía pertenecen al ID estable de contenido. Los enlaces usan post.id en su lugar, porque ese es el slug orientado a URL producido por el content loader:

<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. Las líneas de autoría ya están incluidas en post.data.bylines por la consulta de colección.

Seguir la consulta de publicación

La ruta dinámica de publicación lee el slug de la URL y llama a getEmDashEntry(). El siguiente extracto muestra la consulta esencial y la ruta de renderizado, mientras que la plantilla completa también maneja SEO, líneas de autoría, 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.redirect("/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.redirect("/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 medios seleccionado por el editor y genera salida responsive. PortableText convierte los datos de bloque almacenados en encabezados, párrafos, enlaces, imágenes, bloques de código y los otros tipos de bloque soportados.

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 de rutas estáticas creada durante la compilación.

Usar categorías y etiquetas

La plantilla incluye rutas de archivo para cada categoría y etiqueta. Una ruta de categoría primero resuelve el slug del término, luego filtra 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.redirect("/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 términos y asignaciones en el admin; la guía de Taxonomías cubre categorías jerárquicas, etiquetas planas y taxonomías personalizadas.

getEmDashEntry() incluye los términos asignados de la publicación, por lo 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 cada publicación publicada. Cuando el archivo crezca, agrega un limit y usa paginación por offset para rutas numeradas como /posts/page/2, o paginación por cursor para un enlace de Publicaciones anteriores. Mantén orderBy: { published_at: "desc" } sin cambios entre páginas para que las entradas no cambien de orden inesperadamente.

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(), formatea cada fecha de publicación y escapa el título y extracto antes de insertarlos en XML. También lee el título del sitio y el eslogan de la configuración de EmDash.

Después de publicar la publicación de prueba, abre /rss.xml y busca su título. Si el sitio usará una URL de producción absoluta en feeds, establece la opción site de Astro antes del despliegue; el endpoint recurre al origen de la solicitud actual durante el desarrollo local.

En este punto, el blog tiene un flujo de trabajo de autoría, páginas de publicaciones en tiempo de ejecución, archivos de taxonomía, renderizado de medios y un feed. Continúa con Consultar Contenido para filtros y paginación, o Biblioteca de Medios para edición de assets y seguimiento de uso. Para redactar o editar publicaciones con un asistente de IA, sigue Herramientas de IA.