Saltearse al contenido

Tutorial - Ampliar con colecciones de contenido.

Las colecciones de contenido son una forma poderosa de gestionar grupos de contenido similar, como lo son las publicaciones de un blog. Las colecciones ayudan a organizar tus documentos, validar el contenido de los archivos YAML y proveen seguridad de tipos con TypeScript automáticamente para todo tu contenido (incluso si no escribes nada de TypeScript por tu cuenta).

Prepárate para...

  • Mover el directorio de publicaciones del blog en el directorio src/content/
  • Crear un esquema que defina el contenido de la publicación en términos de frontmatter.
  • Utilizar el método getCollection() para obtener el contenido de tus publicaciones y sus metadatos.

Necesitarás un proyecto existente de Astro que incluya archivos Markdown o MDX en el directorio src/pages/.

Este tutorial utiliza el proyecto de Crear un Blog terminado para demostrar cómo convertir un blog en colecciones de contenido. Puedes clonar y usar la base de código localmente, o completar el tutorial en tu navegador editando el código del tutorial del blog en StackBlitz.

Si lo prefieres, puedes seguir los pasos usando tus propios proyectos, pero, necesitarás realizar algunos ajustes en las instrucciones del código.

Nosotros recomendamos usar nuestro proyecto de ejemplo para terminar este tutorial corto. Después, puedes utilizar lo aprendido para crear colecciones de contenido en tu proyecto.

En el tutorial introductorio de Crear un Blog, aprendiste acerca del sistema de rutas basado en archivos: cualquier archivo .astro, .md, o .mdx dentro del directorio src/pages/ se convertirá automáticamente en una página en tu sitio.

Para crear tu primera publicación https://example.com/posts/post-1/, creaste el directorio /posts/ y agregaste el archivo post-1.md. Después, agregaste un archivo Markdown en este directorio cada que querías agregar un nuevo publicación en tu blog.

Incluso usando colecciones de contenido deberás usar el directorio src/pages/ para cada página individual, tal como la página “Acerca De”. Pero, mover el contenido de tus publicaciones al directorio src/content/ te permitirá utilizar APIs con más características y de mejor rendimiento para generar el indexado de tu blog y mostrar las publicaciones de manera individual.

Al mismo tiempo, recibirás guía y dispondrás autocompletado en tu editor de código porque usarás un esquema para definir la estructura común para cada publicación que Astro te ayudará a implementar. En tu esquema, puedes especificar las propiedades de frontmatter que son necesarias, como la descripción, el autor, y que tipo de dato debe tener cada propiedad, por ejemplo, si es una cadena de texto o un arreglo. Esto ayuda a evitar muchos errores de manera temprana, usando errores descriptivos que te dirán cual es el problema exacto.

Lee más sobre las colecciones de contenido de Astro en nuestra guía, o comienza con las instrucciones a continuación para convertir un blog básico de src/pages/posts/ a src/content/posts/.

  1. ¿Qué tipo de página guardarías en el directorio src/pages/?

  2. ¿Cuál no es una ventaja de mover las publicaciones de un blog a una colección de contenido?

  3. Las colecciones de contenido usan TypeScript…

Extendiendo el tutorial del blog con colecciones de contenido.

Sección titulada Extendiendo el tutorial del blog con colecciones de contenido.

Los siguientes pasos mostrarán cómo extender el resultado del tutorial Crear un Blog creando colecciones de contenido para las publicaciones del blog.

  1. Actualiza a la última versión de Astro y actualiza todas las integraciones a sus últimas versiones ejecutando los siguiente comandos en tu terminal:

    Ventana de terminal
    # Actualiza a Astro v4.x
    npm install astro@latest
    # Ejemplo: Actualiza la integración de Preact
    npm install @astrojs/preact@latest
  2. El blog utiliza la configuración base de TypeScript (la menos estricta). Para utilizar las colecciones de contenido debes configurar TypeScript, también deberás usar la configuración estricta strict o la más estricta strictest, o agregar dos opciones en el archivo tsconfig.json.

    Para utilizar colecciones de contenido sin escribir nada de TypeScript, agrega las siguientes dos líneas al archivo de configuración:

    tsconfig.json
    {
    // Nota: No se necesitan cambios si se utiliza "astro/tsconfigs/strict" o "astro/tsconfigs/strictest"
    "extends": "astro/tsconfigs/base",
    "compilerOptions": {
    "strictNullChecks": true,
    "allowJs": true
    }
    }

Crear una colección para tus publicaciones del blog.

Sección titulada Crear una colección para tus publicaciones del blog.
  1. Crea una nueva colección (directorio) llamado src/content/posts/.

  2. Mueve todas las publicaciones de tu blog (archivos .md) de src/pages/posts/ en esta nueva colección.

  3. Crea el archivo src/content/config.ts para definir un esquema para la colección postsCollection. Para el código que existe en este tutorial, agrega el siguiente contenido al archivo para definir todas las propiedades del frontmatter utilizadas en las entradas de blog:

    src/content/config.ts
    // Importa las utilidades de `astro:content`
    import { z, defineCollection } from "astro:content";
    // Define un `type` y un `schema` para cada colección
    const postsCollection = defineCollection({
    type: 'content',
    schema: z.object({
    title: z.string(),
    pubDate: z.date(),
    description: z.string(),
    author: z.string(),
    image: z.object({
    url: z.string(),
    alt: z.string()
    }),
    tags: z.array(z.string())
    })
    });
    // Exporta un solo objeto `collections` con las colecciones registradas
    export const collections = {
    posts: postsCollection,
    };
  4. Para que astro reconozca tu esquema, abandona el servidor de desarrollo (CTRL + C) y ejecuta el siguiente comando npx astro sync. Esto definirá el módulo astro:content para la API de Colecciones de Contenido. Reinicia el servidor de desarrollo para continuar con el tutorial.

Generando las páginas de una colección

Sección titulada Generando las páginas de una colección
  1. Crea un archivo de página llamado src/pages/posts/[...slug].astro. Los archivos Markdown y MDX no se convertirán en páginas automáticamente usando el sistema de rutas basado en archivos cuando estos viven en una colección, por eso debes crear una página encargada de generar un blog individual para cada publicación.

  2. Agrega el siguiente código para consultar tu colección, para hacer que el slug y el contenido de cada publicación del blog estén disponibles para cada página que se genere:

    src/pages/posts/[...slug].astro
    ---
    import { getCollection } from 'astro:content';
    import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';
    export async function getStaticPaths() {
    const blogEntries = await getCollection('posts');
    return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
    }));
    }
    const { entry } = Astro.props;
    const { Content } = await entry.render();
    ---
  3. La etiqueta <Content /> renderiza tu publicación con el layout para archivos Markdown. Esto te permite especificar una plantilla común para todas tus publicaciones.

    src/pages/posts/[...slug].astro
    ---
    import { getCollection } from 'astro:content';
    import MarkdownPostLayout from '../../layouts/MarkdownPostLayout.astro';
    export async function getStaticPaths() {
    const blogEntries = await getCollection('posts');
    return blogEntries.map(entry => ({
    params: { slug: entry.slug }, props: { entry },
    }));
    }
    const { entry } = Astro.props;
    const { Content } = await entry.render();
    ---
    <MarkdownPostLayout frontmatter={entry.data}>
    <Content />
    </MarkdownPostLayout>
  4. Elimina la definición de layout del frontmatter de cada publicación. Ahora, tu contenido está envuelto en un layout al renderizarse, y esta propiedad ya no es necesaria.

    src/content/posts/post-1.md
    ---
    layout: ../../layouts/MarkdownPostLayout.astro
    title: 'Mi primera entrada en el blog'
    pubDate: 2022-07-01
    ...
    ---

Reemplaza Astro.glob() con getCollection()

Sección titulada Reemplaza Astro.glob() con getCollection()
  1. En cualquier lugar donde tengas una lista de publicaciones de blog, como la página de blog del tutorial (src/pages/blog.astro/), deberás reemplazar Astro.glob() con getCollection() como la forma de obtener contenido y metadatos de tus archivos Markdown.

    src/pages/blog.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";
    const pageTitle = "Mi blog de Astro sobre aprendizaje";
    const allPosts = await Astro.glob("../pages/posts/*.md");
    const allPosts = await getCollection("posts");
    ---
  2. También necesitarás actualizar las referencias de los datos regresados por cada post. Ahora encontrarás los valores de frontmatter en la propiedad data de cada objeto. Adicional a esto, cuando utilizas colecciones, cada objeto post tendrá un slug por página, no una URL completa.

    src/pages/blog.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../layouts/BaseLayout.astro";
    import BlogPost from "../components/BlogPost.astro";
    const pageTitle = "My Astro Learning Blog";
    const allPosts = await getCollection("posts");
    ---
    <BaseLayout pageTitle={pageTitle}>
    <p>Aquí es donde voy a publicar sobre mi viaje de aprendizaje con Astro.</p>
    <ul>
    {
    allPosts.map((post) => (
    <BlogPost url={post.url} title={post.frontmatter.title} />)}
    <BlogPost url={`/posts/${post.slug}/`} title={post.data.title} />
    ))
    }
    </ul>
    </BaseLayout>
  3. El tutorial del proyecto de blog también genera dinámicamente una página para cada etiqueta src/pages/tags/[tag].astro y muestra una lista de etiquetas src/pages/tags/index.astro.

    Aplica los mismos cambios de arriba a los siguientes dos archivos:

    • Obtén los datos de toda tu colección de publicaciones usando getCollection("posts") en vez de usar Astro.glob()
    • Accede a las propiedades de frontmatter a través del objeto data en vez de frontmatter
    • Crea una URL de página agregando el slug de la publicación a la ruta /posts/

    La página encargada de gestionar las páginas individuales para cada etiqueta ahora tendrá los siguientes cambios:

    src/pages/tags/[tag].astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../../layouts/BaseLayout.astro";
    import BlogPost from "../../components/BlogPost.astro";
    export async function getStaticPaths() {
    const allPosts = await getCollection("posts");
    const uniqueTags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
    return uniqueTags.map((tag) => {
    const filteredPosts = allPosts.filter((post) =>
    post.data.tags.includes(tag)
    );
    return {
    params: { tag },
    props: { posts: filteredPosts },
    };
    });
    }
    const { tag } = Astro.params;
    const { posts } = Astro.props;
    ---
    <BaseLayout pageTitle={tag}>
    <p>Artículos con la etiqueta {tag}</p>
    <ul>
    { posts.map((post) => <BlogPost url={`/posts/${post.slug}/`} title={post.data.title} />) }
    </ul>
    </BaseLayout>

    Inténtalo tú mismo - Actualiza la consulta en la página de indexado de etiquetas.

    Sección titulada Inténtalo tú mismo - Actualiza la consulta en la página de indexado de etiquetas.

    Importa y utiliza getCollection para obtener las etiquetas en las publicaciones del blog en src/pages/tags/index.astro siguiendo los mismos pasos de arriba.

    Muéstrame el código.
    src/pages/tags/index.astro
    ---
    import { getCollection } from "astro:content";
    import BaseLayout from "../../layouts/BaseLayout.astro";
    const allPosts = await getCollection("posts");
    const tags = [...new Set(allPosts.map((post) => post.data.tags).flat())];
    const pageTitle = "Índice de etiquetas";
    ---
    ...

Actualiza cualquier valor del frontmatter para que coincida con tu esquema

Sección titulada Actualiza cualquier valor del frontmatter para que coincida con tu esquema
  1. Si es necesario, actualiza los valores del frontmatter que no coincidan con el esquema de tu colección en todo tu proyecto, al igual que en tu plantilla o layout.

    En el ejemplo tutorial de blog, pubDate era una cadena de texto. Ahora, con el nuevo esquema definido para frontmatter, pubDate será un objeto de tipo Date.

    Para renderizar la fecha en la plantilla de la publicación, necesitamos convertirla a cadena de texto:

    src/layouts/MarkdownPostLayout.astro
    ...
    <BaseLayout pageTitle={frontmatter.title}>
    <p>{frontmatter.pubDate.toString().slice(0,10)}</p>
    <p><em>{frontmatter.description}</em></p>
    <p>Escrito por: {frontmatter.author}</p>
    <img src={frontmatter.image.url} width="300" alt={frontmatter.image.alt} />
    ...
  1. Finalmente, el tutorial de blog incluye el feed RSS. Esta función también debe utilizar getCollection() para regresar la información de tus publicaciones. También deberás generar los artículos utilizando la propiedad data.

    src/pages/rss.xml.js
    import rss from '@astrojs/rss';
    import { pagesGlobToRssItems } from '@astrojs/rss';
    import { getCollection } from 'astro:content';
    export async function GET(context) {
    const posts = await getCollection("posts");
    return rss({
    title: 'Aprendiz de Astro | Blog',
    description: 'Mi viaje aprendiendo Astro',
    site: context.site,
    items: await pagesGlobToRssItems(import.meta.glob('./**/*.md')),
    items: posts.map((post) => ({
    title: post.data.title,
    pubDate: post.data.pubDate,
    description: post.data.description,
    link: `/posts/${post.slug}/`,
    })),
    customData: `<language>en-us</language>`,
    })
    }

Para ver el ejemplo completo del tutorial usando colecciones de contenido, consulta la rama de Colecciones de Contenido del repositorio del tutorial.