Cambiar tema

Guía completa de Astro Content Collections: del concepto a la validación con Schema

Easton editorial illustration: performance inspection lens

La página de inicio del blog se cayó: el error decía que el campo publishDate de un artículo tenía un formato incorrecto. Tardé media hora en encontrar el archivo — la fecha estaba escrita como 2024/12/01 en lugar de 2024-12-01. Y eso con un blog de solo 30 artículos; con cientos, cada vez que añades un campo nuevo tendrías que revisar todos los archivos a mano.

Content Collections resuelve exactamente esto: deja que Astro detecte errores de contenido como TypeScript detecta errores de código. Tras configurar el Schema, el editor ofrece sugerencias inteligentes y no tienes que ir al documento a buscar nombres de campo. En este artículo verás qué son Content Collections, cómo escribir el archivo de configuración y cómo usar la validación con Schema.

¿Qué son Content Collections y por qué las necesitas?

Quizá pienses: ¿Content Collections no es solo gestionar carpetas de Markdown? ¿No puedo crear una carpeta blog/ bajo src/pages/ y tener un blog igual?

Sí, a nivel funcional puedes. Pero el problema es que ese enfoque no ofrece protección de tipos seguros.

Con el método tradicional, el frontmatter de tu Markdown se ve así:


---

title: "Título de mi blog"
date: "2024-12-01"
tags: ["Astro", "tutorial"]

---

Contenido del artículo...

Parece correcto, ¿verdad? Pero piensa en estos casos:

  • En un artículo escribes tag en lugar de tags (te falta la s)
  • Pones la fecha como 12/01/2024 en lugar de 2024-12-01
  • Añades un campo author pero olvidas completarlo en varios artículos antiguos

Astro no te avisa antes. Solo en tiempo de ejecución, cuando la página falla al renderizar, descubres dónde está el problema.

Content Collections existe para resolver esto. En esencia es un sistema de gestión de contenido con tipos seguros. Puedes verlo como añadir comprobación de tipos TypeScript a los archivos Markdown.

En concreto, Content Collections ofrece:

  1. Validación con Schema: define tipos y estructura del frontmatter; si no cumple, falla directamente
  2. Generación automática de tipos: genera tipos TypeScript a partir del Schema; el editor ofrece sugerencias inteligentes
  3. API de consulta unificada: consulta contenido con getCollection() y métodos similares; devuelve datos type-safe
  4. Optimización de rendimiento: la Content Layer API de Astro 5.0 hace las consultas más rápidas

En resumen: el enfoque tradicional es «libre pero inseguro»; Content Collections es «con restricciones pero fiable». Inviertes un poco más de tiempo configurando el Schema y evitas el 99 % de errores tontos.

Siendo sincero, todos mis proyectos Astro usan Content Collections. Lo configuras una vez y te beneficia todo el proyecto.

Configuración práctica de Content Collections

Bien, teoría aparte, vamos a configurarlo. El flujo son tres pasos: crear directorios, escribir configuración y crear contenido.

Paso 1: crear directorios

Content Collections exige que el contenido esté en src/content/. Es un directorio reservado de Astro (desde v2.0), pensado para colecciones de contenido.

La estructura suele ser así:

src/
├── content/
│   ├── blog/          # Colección de blog
│   │   ├── post-1.md
│   │   └── post-2.md
│   └── docs/          # Colección de documentación
│       ├── guide-1.md
│       └── guide-2.md
├── content.config.ts   # Archivo de configuración (fíjate en la ubicación)
└── pages/
    └── ...

Nota: el archivo de configuración es src/content.config.ts (o .js, .mjs), no va dentro de content/. Al principio yo también lo puse mal y perdí un buen rato buscando el fallo.

Cada subdirectorio es una colección (Collection). Por ejemplo, src/content/blog/ es la colección blog y src/content/docs/ es docs.

Paso 2: escribir el archivo de configuración

Crea src/content.config.ts; es el núcleo de Content Collections:

// src/content.config.ts
import { defineCollection, z } from 'astro:content';

// Definir la colección blog
const blogCollection = defineCollection({
  type: 'content',  // Tipo: content indica archivos Markdown/MDX
  schema: z.object({
    title: z.string(),                    // Título (obligatorio)
    description: z.string(),              // Descripción (obligatorio)
    pubDate: z.coerce.date(),             // Fecha de publicación (se convierte a Date)
    tags: z.array(z.string()).optional(), // Array de etiquetas (opcional)
    draft: z.boolean().default(false),    // Estado de borrador (por defecto false)
  }),
});

// Exportar el objeto collections
export const collections = {
  'blog': blogCollection,  // La clave corresponde al nombre del directorio
};

Este código puede parecer denso; lo desglosamos:

  1. defineCollection(): define la configuración de una colección
  2. type: 'content': indica que es una colección de archivos Markdown/MDX
  3. schema: define la estructura del frontmatter con Zod (librería de validación)
  4. Objeto collections: exporta la configuración; la clave debe coincidir con el nombre del directorio

La clave está en schema. Cada campo se define con z.xxx():

  • z.string(): tipo cadena
  • z.coerce.date(): convierte automáticamente cadenas en objetos Date
  • z.array(z.string()): array de cadenas
  • .optional(): campo opcional
  • .default(false): valor por defecto

Paso 3: crear archivos de contenido

Tras configurar, puedes crear archivos Markdown en src/content/blog/:


---

title: "Introducción a Astro Content Collections"
description: "Aprende a configurar y usar Content Collections"
pubDate: "2024-12-01"
tags: ["Astro", "tutorial"]

---

Este es el contenido del artículo...

Si el frontmatter cumple el Schema, Astro lo parsea sin problema. Si algún campo no cumple (p. ej. pubDate con formato incorrecto), Astro falla en tiempo de compilación.

Consultar datos en las páginas

Tras configurar, puedes consultar contenido en cualquier archivo Astro:


---

// src/pages/blog/index.astro
import { getCollection } from 'astro:content';

// Obtener todos los artículos del blog
const allPosts = await getCollection('blog');

// Filtrar borradores (draft: true)
const publishedPosts = allPosts.filter(post => !post.data.draft);

---

<ul>
  {publishedPosts.map(post => (
    <li>
      <a href={`/blog/${post.slug}`}>
        {post.data.title}
      </a>
      <p>{post.data.description}</p>
    </li>
  ))}
</ul>

Fíjate: post.data son los datos del frontmatter y tienen sugerencias de tipos TypeScript completas. En VS Code, al escribir post.data., el editor sugiere title, description, pubDate y demás campos.

Esa es la gracia de Content Collections: tipos seguros + sugerencias del editor; la experiencia de escribir código sube de nivel.

Validación con Schema en profundidad

En la sección anterior usamos tipos básicos como z.string() y z.coerce.date(). Pero la validación con Schema va mucho más allá. Aquí profundizamos en Zod.

Referencia rápida de tipos básicos

Los más habituales:

import { z } from 'astro:content';

z.string()           // Cadena
z.number()           // Número
z.boolean()          // Booleano
z.date()             // Objeto Date
z.coerce.date()      // Convierte automáticamente cadenas en Date
z.array(z.string())  // Array de cadenas
z.enum(['draft', 'published'])  // Enumeración (solo valores permitidos)

z.coerce.date() es especialmente útil. En el frontmatter Markdown las fechas suelen ser cadenas ("2024-12-01"). Con z.date() fallaría porque exige un objeto Date. z.coerce.date() convierte por ti y ahorra trabajo.

Campos opcionales y valores por defecto

No todos los campos son obligatorios. Por ejemplo, tags puede no hacer falta en algunos artículos. Usa .optional():

schema: z.object({
  title: z.string(),                    // Obligatorio
  tags: z.array(z.string()).optional(), // Opcional
  draft: z.boolean().default(false),    // Con valor por defecto
})

.default() es práctico: si el frontmatter no incluye el campo, Astro rellena el valor por defecto.

Uso avanzado: validación de imágenes

Astro ofrece el tipo image() para validar rutas de imagen:

import { defineCollection, z } from 'astro:content';

const blogCollection = defineCollection({
  schema: ({ image }) => z.object({  // Nota: aquí se usa forma de función
    title: z.string(),
    cover: image(),  // Validación de ruta de imagen
  }),
});

image() comprueba que la ruta apunte a un archivo de imagen válido (admite rutas relativas). Muy útil para portadas en la página de inicio del blog.

Referenciar otras colecciones: z.reference()

A veces el contenido se relaciona entre sí. Por ejemplo, un artículo pertenece a una categoría que también es una colección. Usa z.reference():

// Definir colección de categorías
const categoryCollection = defineCollection({
  schema: z.object({
    name: z.string(),
    slug: z.string(),
  }),
});

// Colección blog que referencia categorías
const blogCollection = defineCollection({
  schema: z.object({
    title: z.string(),
    category: z.reference('category'),  // Referencia la colección category
  }),
});

export const collections = {
  'category': categoryCollection,
  'blog': blogCollection,
};

En el frontmatter del artículo, el campo category solo necesita el nombre del archivo de categoría (sin extensión):


---

title: "Mi blog"
category: "tech"  # Referencia src/content/category/tech.md

---

Astro valida que la categoría exista y el tipo también es seguro.

Objetos anidados complejos

Si el frontmatter es complejo, puedes anidar objetos:

schema: z.object({
  title: z.string(),
  author: z.object({
    name: z.string(),
    email: z.string().email(),  // Valida formato de email
    avatar: z.string().url(),   // Valida formato de URL
  }),
  seo: z.object({
    keywords: z.array(z.string()),
    description: z.string().max(160),  // Limita longitud máxima
  }).optional(),
})

Frontmatter correspondiente:


---

title: "Título del artículo"
author:
  name: "Zhang San"
  email: "[email protected]"
  avatar: "https://example.com/avatar.jpg"
seo:
  keywords: ["Astro", "tutorial"]
  description: "Este es un tutorial sobre Astro"

---

La magia de los tipos seguros: inferencia automática de TypeScript

Tras configurar el Schema, Astro genera tipos TypeScript. Al consultar datos, el editor ofrece sugerencias completas:

import { getCollection } from 'astro:content';

const posts = await getCollection('blog');

posts.forEach(post => {
  // El editor sugiere todos los campos bajo post.data
  console.log(post.data.title);       // ✅ Tipo: string
  console.log(post.data.pubDate);     // ✅ Tipo: Date
  console.log(post.data.tags);        // ✅ Tipo: string[] | undefined
  console.log(post.data.notExist);    // ❌ Error de compilación: el campo no existe
});

Es lo mejor de Content Collections: no escribes tipos a mano; Astro los genera del Schema con precisión.

getEntry() frente a getCollection()

Por último, la diferencia entre las APIs de consulta:

  • getCollection('blog'): obtiene todo el contenido de la colección
  • getEntry('blog', 'my-post'): obtiene una entrada concreta por slug

La consulta individual es más eficiente, ideal para páginas de detalle:


---

// src/pages/blog/[slug].astro
import { getEntry } from 'astro:content';

const { slug } = Astro.params;
const post = await getEntry('blog', slug);

if (!post) {
  return Astro.redirect('/404');
}

const { Content } = await post.render();

---

<article>
  <h1>{post.data.title}</h1>
  <Content />
</article>

Siendo sincero, al principio la sintaxis de Zod me desconcertó un poco. Tras usarla unas veces, se hace natural; además los mensajes de error son claros y fáciles de depurar.

Problemas habituales y soluciones

Al configurar Content Collections es normal toparse con errores. Aquí recopilo los más frecuentes y cómo resolverlos — todos los he pisado yo.

Error 1: MarkdownContentSchemaValidationError

Es el más común: el frontmatter no cumple el Schema. El mensaje suele ser así:

blog → my-post.md frontmatter does not match collection schema.
- "title" is required
- "pubDate" must be a valid date

¿Cómo leer este error?

Astro indica claramente qué archivo (my-post.md) y qué campos (title, pubDate) fallan.

Causas habituales y soluciones:

  1. Campo faltante: el Schema define un campo obligatorio que no está en el frontmatter

    • Solución: añade el campo o usa .optional() en el Schema
  2. Error de ortografía en el nombre del campo: p. ej. publishDate en lugar de pubDate

    • Solución: unifica los nombres; usa autocompletado del editor
  3. Tipo que no coincide: el Schema exige z.number() pero el frontmatter tiene una cadena

    • Solución: comprueba el formato del valor

Error 2: InvalidContentEntryFrontmatterError

Indica que el frontmatter tiene un problema de formato (error de sintaxis YAML); ni siquiera se puede parsear.

Causa habitual:


---

title: "Mi título
description: "Olvidé cerrar las comillas"

---

Solución: revisa la sintaxis YAML, sobre todo comillas, dos puntos y sangría. Recomendado: plugin del editor con comprobación YAML.

Error 3: problemas de formato de fecha

Este me ha pillado varias veces. Si usas z.date() en lugar de z.coerce.date(), Astro exige un objeto Date en el frontmatter, pero en YAML solo puedes escribir cadenas.

Solución: en el Schema usa z.coerce.date(); convierte automáticamente cadenas en Date:

// ❌ Incorrecto: exige objeto Date, pero en frontmatter hay cadenas
pubDate: z.date()

// ✅ Correcto: convierte automáticamente cadenas en Date
pubDate: z.coerce.date()

Gestionar datos heredados: .passthrough()

Si el blog ya tiene muchos artículos antiguos con frontmatter inconsistente, puedes usar .passthrough() para relajar temporalmente la validación:

schema: z.object({
  title: z.string(),
  // ... otros campos
}).passthrough()  // Permite campos adicionales no definidos

Es solo una solución provisional. A largo plazo conviene unificar la estructura del frontmatter.

Escenario con varias colecciones: cómo organizar

Si el sitio tiene blog, documentación, casos de estudio, etc., crea varias colecciones:

src/content/
├── blog/
├── docs/
└── case-studies/

Luego defínelas por separado en content.config.ts:

const blogCollection = defineCollection({ /* ... */ });
const docsCollection = defineCollection({ /* ... */ });
const caseStudiesCollection = defineCollection({ /* ... */ });

export const collections = {
  'blog': blogCollection,
  'docs': docsCollection,
  'case-studies': caseStudiesCollection,
};

Cada colección puede tener un Schema distinto sin interferir entre sí.

Mejores prácticas de diseño del Schema

Resumen de mi experiencia:

  1. Pocos campos obligatorios: solo los realmente necesarios; el resto con .optional() o .default()
  2. Fechas con z.coerce.date(): evita conversiones manuales
  3. Nombres en camelCase: pubDate encaja mejor con JavaScript que pub_date
  4. Dividir objetos complejos: si el frontmatter es muy complejo, considera varias colecciones con z.reference()
  5. Comentarios claros: en el Schema, documenta el propósito de cada campo para el equipo

Lista de comprobación (para depurar)

Si hay error, revisa en este orden:

  • ¿Existe el directorio src/content/?
  • ¿Está src/content.config.ts en la ubicación correcta? (fuera de content/)
  • ¿Las claves del objeto collections exportado coinciden con los nombres de directorio?
  • ¿Es correcta la sintaxis YAML del frontmatter? (comillas, dos puntos, sangría)
  • ¿Están todos los campos obligatorios?
  • ¿Coinciden los tipos de campo con el Schema?

Siendo sincero, parece mucho, pero los mensajes de error de Astro son bastante claros. Leyendo el error con atención, sueles localizar el problema rápido.

Conclusión

Hemos vuelto a los tres dolores del principio:

¿No sabías qué son Content Collections? Ahora debería quedar claro: añaden comprobación de tipos TypeScript al contenido Markdown para que Astro detecte errores en compilación, no cuando la página ya se ha caído.

¿Cómo escribir el archivo de configuración? Tres pasos: crea src/content/, el archivo src/content.config.ts y define el Schema con defineCollection() y Zod. Las claves deben coincidir con los nombres de directorio.

¿Cómo usar la validación con Schema? Domina tipos básicos (z.string(), z.coerce.date(), z.array()), aprende .optional() y .default(), y cuando falle algo, lee el mensaje de Astro.

Siendo sincero, Content Collections es una de las funciones de Astro que más merece la pena. Inviertes tiempo al configurar y ahorras horas depurando. Las sugerencias del editor son un gusto; la experiencia de código sube varios escalones.

Próximos pasos

Si quieres probar Content Collections ya:

  1. Proyectos nuevos: configúralo desde el inicio al crear el proyecto Astro; establece convenciones desde el día uno
  2. Proyectos existentes: usa primero .passthrough() para que el contenido actual funcione y unifica el frontmatter poco a poco
  3. Documentación oficial: ante dudas, consulta la documentación oficial de Astro; tiene la referencia completa de la API

Content Collections no es difícil, pero hay que practicar. Leer tutoriales ayuda; escribir el archivo de configuración tú mismo ayuda más. Pruébalo: te gustará esa sensación de tipos seguros.

Flujo completo de configuración de Astro Content Collections

Pasos completos desde la configuración de Content Collections hasta la validación con Schema, para un sistema de gestión de contenido con tipos seguros

⏱️ Estimated time: 30 min

  1. 1

    Step 1: Crear la estructura de directorios

    Crea el directorio src/content/ en la raíz del proyecto:
    • Directorio reservado de Astro (desde v2.0)
    • Dentro de content/, crea subdirectorios como colecciones (p. ej. blog/, docs/)
    • Cada subdirectorio es una colección

    Nota: el archivo de configuración src/content.config.ts no va dentro de content/, sino en src/.
  2. 2

    Step 2: Crear el archivo de configuración

    Crea el archivo src/content.config.ts:

    1. Importa las dependencias:
    import { defineCollection, z } from 'astro:content'

    2. Define la configuración de la colección:
    const blogCollection = defineCollection({
    type: 'content',
    schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    tags: z.array(z.string()).optional(),
    draft: z.boolean().default(false)
    })
    })

    3. Exporta el objeto collections:
    export const collections = { 'blog': blogCollection }
    Nota: la clave debe coincidir con el nombre del directorio
  3. 3

    Step 3: Crear archivos de contenido

    Crea archivos Markdown en src/content/blog/:

    El frontmatter debe cumplir el Schema definido:
    • title (cadena obligatoria)
    • description (cadena obligatoria)
    • pubDate (formato de fecha como "2024-12-01"; z.coerce.date() convierte automáticamente)
    • tags (array de cadenas opcional)
    • draft (booleano opcional, por defecto false)

    Si un campo no cumple el Schema, Astro fallará directamente en tiempo de compilación.
  4. 4

    Step 4: Consultar datos en las páginas

    Importa getCollection en un archivo Astro:
    import { getCollection } from 'astro:content'

    Obtener todos los artículos del blog:
    const allPosts = await getCollection('blog')

    Filtrar borradores:
    const publishedPosts = allPosts.filter(post => !post.data.draft)

    Usar los datos:
    • post.data tiene sugerencias de tipos TypeScript completas
    • El editor sugiere automáticamente title, description, pubDate y demás campos
    • Para una sola entrada, getEntry('blog', slug) es más eficiente
  5. 5

    Step 5: Configuración avanzada del Schema

    Validación de imágenes:
    schema: ({ image }) => z.object({
    cover: image()
    })

    Referenciar otras colecciones:
    z.reference('category')

    Objetos anidados complejos:
    z.object({
    author: z.object({
    name: z.string(),
    email: z.string().email(),
    avatar: z.string().url()
    })
    })

    Gestionar datos heredados:
    .passthrough() permite campos adicionales no definidos

    Escenario con varias colecciones:
    Define por separado blog, docs, case-studies, etc. en el objeto collections
  6. 6

    Step 6: Resolución de errores habituales

    MarkdownContentSchemaValidationError: revisa campos faltantes (añádelos o usa .optional()), errores de ortografía en nombres de campo (unifica los nombres) y tipos que no coinciden (comprueba el formato del valor). InvalidContentEntryFrontmatterError: revisa la sintaxis YAML (comillas, dos puntos, sangría). Problemas de formato de fecha: usa z.coerce.date() en lugar de z.date(). Lista de comprobación: ¿existe el directorio content/? ¿está content.config.ts en la ubicación correcta? ¿coinciden las claves de collections con los nombres de directorio? ¿es correcta la sintaxis YAML? ¿están todos los campos obligatorios? ¿coinciden los tipos de campo con el Schema?

FAQ

¿Qué son Content Collections y por qué las necesitas?
Content Collections es un sistema de gestión de contenido con tipos seguros: en esencia, añade comprobación de tipos TypeScript a los archivos Markdown.

El enfoque tradicional (crear una carpeta blog/ directamente en src/pages/) no ofrece protección de tipos seguros y es fácil que ocurran:
• Errores de ortografía en campos (tags escrito como tag)
• Formato de fecha incorrecto (12/01/2024 en lugar de 2024-12-01)
• Campos nuevos que olvidas añadir en artículos antiguos
• Astro no te avisa antes; solo lo descubres cuando la página falla en tiempo de ejecución

Content Collections ofrece:
1) Validación con Schema (define tipos y estructura del frontmatter; si no cumple, falla directamente)
2) Generación automática de tipos (genera tipos TypeScript a partir del Schema; el editor ofrece sugerencias inteligentes)
3) API de consulta unificada (getCollection() y métodos similares devuelven datos con tipos seguros)
4) Optimización de rendimiento (la Content Layer API de Astro 5.0 hace las consultas más rápidas)

El enfoque tradicional es «libre pero inseguro»; Content Collections es «con restricciones pero fiable». Configúralo una vez y te beneficia todo el proyecto.
¿Cómo configurar Content Collections? ¿Cuál es el flujo completo?
Configuración en tres pasos:

1) Crear directorios:
• Crea src/content/ en la raíz del proyecto (directorio reservado de Astro desde v2.0)
• Dentro de content/, crea subdirectorios como colecciones (p. ej. blog/, docs/); cada subdirectorio es una colección

2) Escribir el archivo de configuración:
• Crea src/content.config.ts (ojo: no va dentro de content/)
• Importa defineCollection y z
• Define la configuración de la colección (type: 'content' indica archivos Markdown/MDX; el schema usa Zod para definir la estructura del frontmatter)
• Exporta el objeto collections (la clave debe coincidir con el nombre del directorio, p. ej. 'blog': blogCollection)

3) Crear archivos de contenido:
• Crea archivos Markdown en src/content/blog/
• El frontmatter debe cumplir el Schema definido; si no, Astro fallará en tiempo de compilación
¿Cómo usar la validación con Schema? ¿Qué tipos son habituales?
Define tipos con Zod:
• z.string() cadena
• z.number() número
• z.boolean() booleano
• z.date() objeto Date
• z.coerce.date() convierte automáticamente cadenas en Date (muy útil: en YAML solo puedes escribir cadenas)
• z.array(z.string()) array de cadenas
• z.enum(['draft', 'published']) enumeración

Campos opcionales y valores por defecto:
• .optional() campo opcional
• .default(false) valor por defecto

Uso avanzado:
• image() para validar rutas de imagen: schema: ({ image }) => z.object({ cover: image() })
• z.reference('category') para referenciar otras colecciones
• Objetos anidados complejos: z.object({ author: z.object({ name: z.string(), email: z.string().email() }) })

Tras configurar el Schema, Astro genera automáticamente tipos TypeScript; el editor ofrece sugerencias completas y todos los campos bajo post.data son type-safe.
¿Cómo consultar datos de Content Collections? ¿Qué diferencia hay entre getCollection y getEntry?
API de consulta:
• getCollection('blog') obtiene todo el contenido de la colección; devuelve un array
• getEntry('blog', 'my-post') obtiene una entrada concreta por slug; devuelve un objeto (más eficiente para páginas de detalle)

Importa en un archivo Astro:
import { getCollection, getEntry } from 'astro:content'

Usar los datos:
• post.data son los datos del frontmatter, con sugerencias de tipos TypeScript completas
• El editor sugiere automáticamente title, description, pubDate y demás campos

Filtrar borradores:
const publishedPosts = allPosts.filter(post => !post.data.draft)

Ejemplo de consulta individual:
const post = await getEntry('blog', slug)
if (!post) return Astro.redirect('/404')
const { Content } = await post.render()
¿Cuáles son los errores habituales de Content Collections y cómo resolverlos?
Errores habituales:

1) MarkdownContentSchemaValidationError (frontmatter no cumple el Schema):
• Revisa campos faltantes (añádelos o usa .optional())
• Errores de ortografía en nombres de campo (unifica los nombres; usa autocompletado del editor)
• Tipos que no coinciden (comprueba el formato del valor)

2) InvalidContentEntryFrontmatterError (error de sintaxis YAML):
• Revisa comillas, dos puntos y sangría
• Recomendado: usa un plugin del editor con comprobación de sintaxis YAML

3) Problemas de formato de fecha:
• Usa z.coerce.date() en lugar de z.date() (en YAML solo puedes escribir cadenas; z.coerce.date() convierte automáticamente)

4) Datos heredados:
• Usa .passthrough() para relajar temporalmente la validación y permitir campos adicionales no definidos; es solo una solución provisional
• A largo plazo, conviene unificar la estructura del frontmatter

Lista de comprobación:
• ¿Existe el directorio content/?
• ¿Está content.config.ts en la ubicación correcta (fuera de content/)?
• ¿Coinciden las claves de collections con los nombres de directorio?
• ¿Es correcta la sintaxis YAML?
• ¿Están todos los campos obligatorios?
• ¿Coinciden los tipos de campo con el Schema?
¿Cómo organizar escenarios con varias colecciones? ¿Cuáles son las mejores prácticas de diseño del Schema?
Escenario con varias colecciones:
• Si el sitio tiene blog, documentación, casos de estudio, etc., crea varias colecciones (src/content/blog/, docs/, case-studies/)
• Defínelas por separado en content.config.ts:
const blogCollection = defineCollection({...})
const docsCollection = defineCollection({...})
• Exporta el objeto collections:
'blog': blogCollection,
'docs': docsCollection,
'case-studies': caseStudiesCollection
• Cada colección puede tener un Schema distinto sin interferir entre sí

Mejores prácticas de diseño del Schema:
1) Pocos campos obligatorios (solo los realmente necesarios; el resto con .optional() o .default())
2) Fechas con z.coerce.date() (evita conversiones manuales)
3) Nombres de campo en camelCase (pubDate encaja mejor con las convenciones de JavaScript que pub_date)
4) Dividir objetos complejos (si el frontmatter es muy complejo, considera varias colecciones relacionadas con z.reference())
5) Comentarios claros (añade comentarios en el Schema para que el equipo entienda el propósito de cada campo)

12 min de lectura · Publicado el: 24 nov 2025 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog