Contentful & Astro
Contentful es un CMS headless que te permite gestionar contenido, integrarte con otros servicios y publicar en múltiples plataformas.
Integración con Astro
Sección titulada "Integración con Astro"En esta sección, utilizaremos el SDK de Contentful para conectar tu espacio de Contentful a Astro con cero JavaScript en el lado del cliente.
Requisitos previos
Sección titulada “Requisitos previos”Para empezar, necesitarás lo siguiente:
-
Un proyecto de Astro - Si aún no tienes un proyecto de Astro, nuestra guía de instalación te pondrá en marcha en poco tiempo.
-
Una cuenta de Contentful y un espacio de Contentful. Si no tienes una cuenta, puedes registrarte para obtener una cuenta gratuita y crear un nuevo espacio de Contentful. También puedes usar un espacio existente si ya tienes uno.
-
Credenciales de Contentful - Puedes encontrar las siguientes credenciales en tu panel de control de Contentful en Settings > API keys. Si no tienes ninguna clave de API, crea una seleccionando Add API key.
- ID de espacio de Contentful - El ID de tu espacio de Contentful.
- Token de acceso de entrega de Contentful - El token de acceso para consumir contenido publicado desde tu espacio de Contentful.
- Token de acceso de vista previa de Contentful - El token de acceso para consumir contenido no publicado desde tu espacio de Contentful.
Configuración de credenciales
Sección titulada "Configuración de credenciales"Para añadir las credenciales de tu espacio de Contentful a Astro, crea un archivo .env en la raíz de tu proyecto con las siguientes variables:
CONTENTFUL_SPACE_ID=YOUR_SPACE_IDCONTENTFUL_DELIVERY_TOKEN=YOUR_DELIVERY_TOKENCONTENTFUL_PREVIEW_TOKEN=YOUR_PREVIEW_TOKENAhora, puedes usar estas variables de entorno en tu proyecto.
Si deseas tener IntelliSense para tus variables de entorno de Contentful, puedes crear un archivo env.d.ts en el directorio src/ y configurar ImportMetaEnv de esta manera:
interface ImportMetaEnv { readonly CONTENTFUL_SPACE_ID: string; readonly CONTENTFUL_DELIVERY_TOKEN: string; readonly CONTENTFUL_PREVIEW_TOKEN: string;}Lee más sobre el uso de variables de entorno y archivos .env en Astro.
Tu directorio raíz ahora debería incluir estos nuevos archivos:
Directoriosrc/
- env.d.ts
- .env
- astro.config.mjs
- package.json
Instalación de dependencias
Sección titulada "Instalación de dependencias"Para conectarte con tu espacio de Contentful, instala ambos de los siguientes usando el único comando a continuación para tu gestor de paquetes preferido:
contentful.js, el SDK oficial de Contentful para JavaScriptrich-text-html-renderer, un paquete para renderizar campos de texto enriquecido de Contentful a HTML.
npm install contentful @contentful/rich-text-html-rendererpnpm add contentful @contentful/rich-text-html-rendereryarn add contentful @contentful/rich-text-html-rendererA continuación, crea un nuevo archivo llamado contentful.ts en el directorio src/lib/ de tu proyecto.
import * as contentful from "contentful";
export const contentfulClient = contentful.createClient({ space: import.meta.env.CONTENTFUL_SPACE_ID, accessToken: import.meta.env.DEV ? import.meta.env.CONTENTFUL_PREVIEW_TOKEN : import.meta.env.CONTENTFUL_DELIVERY_TOKEN, host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",});El fragmento de código anterior crea un nuevo cliente de Contentful, pasando las credenciales del archivo .env.
En el modo de desarrollo, tu contenido se obtendrá de la API de vista previa de Contentful. Esto significa que podrás ver el contenido no publicado desde la aplicación web de Contentful.
Durante la compilación, tu contenido se obtendrá de la API de entrega de Contentful. Esto significa que solo el contenido publicado estará disponible en el momento de la compilación.
Finalmente, tu directorio raíz ahora debería incluir estos nuevos archivos:
Directoriosrc/
- env.d.ts
Directoriolib/
- contentful.ts
- .env
- astro.config.mjs
- package.json
Obtención de datos
Sección titulada "Obtención de datos"Los componentes de Astro pueden obtener datos de tu cuenta de Contentful utilizando contentfulClient y especificando el content_type.
Por ejemplo, si tienes un tipo de contenido “blogPost” que tiene un campo de texto para un título y un campo de texto enriquecido para el contenido, tu componente podría verse así:
---import { contentfulClient } from "../lib/contentful";import { documentToHtmlString } from "@contentful/rich-text-html-renderer";import type { EntryFieldTypes } from "contentful";
interface BlogPost { contentTypeId: "blogPost", fields: { title: EntryFieldTypes.Text content: EntryFieldTypes.RichText, }}
const entries = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost",});---<body> {entries.items.map((item) => ( <section> <h2>{item.fields.title}</h2> <article set:html={documentToHtmlString(item.fields.content)}></article> </section> ))}</body>Si tienes un espacio de Contentful vacío, consulta configurar un modelo de Contentful para aprender cómo crear un modelo de blog básico para tu contenido.
Puedes encontrar más opciones de consulta en la documentación de Contentful.
Crear un blog con Astro y Contentful
Sección titulada “Crear un blog con Astro y Contentful”Con la configuración anterior, ahora puedes crear un blog que use Contentful como CMS.
Requisitos previos
Sección titulada “Requisitos previos”- Un espacio de Contentful - Para este tutorial recomendamos comenzar con un espacio vacío. Si ya tienes un modelo de contenido, siéntete libre de usarlo, pero tendrás que modificar nuestros fragmentos de código para que coincidan con tu modelo de contenido.
- Un proyecto de Astro integrado con el SDK de Contentful - Consulta integrar con Astro para obtener más detalles sobre cómo configurar un proyecto de Astro con Contentful.
Configurar un modelo de Contentful
Sección titulada “Configurar un modelo de Contentful”Dentro de tu espacio de Contentful, en la sección Content model, crea un nuevo modelo de contenido con los siguientes campos y valores:
- Nombre: Publicación de blog
- API identifier:
blogPost - Descripción: Este tipo de contenido es para una publicación de blog
En tu tipo de contenido recién creado, utiliza el botón Add Field para añadir 5 campos nuevos con los siguientes parámetros:
- Text field
- Nombre: title
- Identificador de API:
title(deja los otros parámetros con sus valores por defecto)
- Date and time field
- Nombre: date
- API identifier:
date
- Text field
- Nombre: slug
- Identificador de API:
slug(deja los otros parámetros con sus valores por defecto)
- Text field
- Nombre: description
- API identifier:
description
- Rich text field
- Nombre: content
- API identifier:
content
Haz clic en Save para guardar tus cambios.
En la sección Content de tu espacio de Contentful, crea una nueva entrada haciendo clic en el botón Add Entry. Luego, completa los campos:
- Title:
Astro is amazing! - Slug:
astro-is-amazing - Descripción:
Astro es un nuevo generador de sitios estáticos que es extremadamente rápido y fácil de usar. - Date:
2022-10-05 - Content:
This is my first blog post!
Haz clic en Publish para guardar tu entrada. Acabas de crear tu primera publicación de blog.
Siéntete libre de añadir tantas publicaciones de blog como desees, luego cambia a tu editor de código favorito para empezar a programar con Astro!
Mostrar una lista de publicaciones de blog
Sección titulada “Mostrar una lista de publicaciones de blog”Crea una nueva interfaz llamada BlogPost y añádela a tu archivo contentful.ts en src/lib/. Esta interfaz coincidirá con los campos de tu tipo de contenido de publicación de blog en Contentful. La utilizarás para tipar la respuesta de tus entradas de publicación de blog.
import * as contentful from "contentful";import type { EntryFieldTypes } from "contentful";
export interface BlogPost { contentTypeId: "blogPost", fields: { title: EntryFieldTypes.Text content: EntryFieldTypes.RichText, date: EntryFieldTypes.Date, description: EntryFieldTypes.Text, slug: EntryFieldTypes.Text }}
export const contentfulClient = contentful.createClient({ space: import.meta.env.CONTENTFUL_SPACE_ID, accessToken: import.meta.env.DEV ? import.meta.env.CONTENTFUL_PREVIEW_TOKEN : import.meta.env.CONTENTFUL_DELIVERY_TOKEN, host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",});A continuación, ve a la página de Astro donde obtendrás los datos de Contentful. Utilizaremos la página de inicio index.astro en src/pages/ en este ejemplo.
Importa la interfaz BlogPost y contentfulClient desde src/lib/contentful.ts.
Obtén todas las entradas de Contentful con un tipo de contenido de blogPost mientras pasas la interfaz BlogPost para tipar tu respuesta.
---import { contentfulClient } from "../lib/contentful";import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost",});---Esta llamada de obtención devolverá un array con tus publicaciones de blog en entries.items. Puedes utilizar map() para crear un nuevo array (posts) que dé formato a los datos devueltos.
El siguiente ejemplo devuelve las propiedades items.fields de nuestro modelo de contenido para crear una vista previa de la publicación del blog y, al mismo tiempo, reformatea la fecha a un formato más legible.
---import { contentfulClient } from "../lib/contentful";import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost",});
const posts = entries.items.map((item) => { const { title, date, description, slug } = item.fields; return { title, slug, description, date: new Date(date).toLocaleDateString() };});---Finalmente, puedes usar posts en tu plantilla para mostrar una vista previa de cada publicación de blog.
---import { contentfulClient } from "../lib/contentful";import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost",});
const posts = entries.items.map((item) => { const { title, date, description, slug } = item.fields; return { title, slug, description, date: new Date(date).toLocaleDateString() };});---<html lang="en"> <head> <title>My Blog</title> </head> <body> <h1>My Blog</h1> <ul> {posts.map((post) => ( <li> <a href={`/posts/${post.slug}/`}> <h2>{post.title}</h2> </a> <time>{post.date}</time> <p>{post.description}</p> </li> ))} </ul> </body></html>Generación de publicaciones de blog individuales
Sección titulada “Generación de publicaciones de blog individuales”Utiliza el mismo método para obtener tus datos de Contentful que se muestra arriba, pero esta vez, en una página que creará una ruta de página única para cada publicación de blog.
Generación de sitios estáticos
Sección titulada "Generación de sitios estáticos"Si estás utilizando el modo estático por defecto de Astro, utilizarás rutas dinámicas y la función getStaticPaths(). Esta función se llamará en el momento de la compilación para generar la lista de rutas que se convertirán en páginas.
Crea un nuevo archivo llamado [slug].astro en src/pages/posts/.
Al igual que hiciste en index.astro, importa la interfaz BlogPost y contentfulClient desde src/lib/contentful.ts.
Esta vez, obtén tus datos dentro de una función getStaticPaths().
---import { contentfulClient } from "../../lib/contentful";import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() { const entries = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost", });}---Luego, mapea cada elemento a un objeto con una propiedad params y otra props. La propiedad params se utilizará para generar la URL de la página y la propiedad props se pasará al componente de la página como props.
---import { contentfulClient } from "../../lib/contentful";import { documentToHtmlString } from "@contentful/rich-text-html-renderer";import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() { const entries = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost", });
const pages = entries.items.map((item) => ({ params: { slug: item.fields.slug }, props: { title: item.fields.title, content: documentToHtmlString(item.fields.content), date: new Date(item.fields.date).toLocaleDateString(), }, })); return pages;}---La propiedad dentro de params debe coincidir con el nombre de la ruta dinámica. Dado que nuestro nombre de archivo es [slug].astro, utilizamos slug.
En nuestro ejemplo, el objeto props pasa tres propiedades a la página:
- title (una cadena)
- content (un documento de texto enriquecido convertido a HTML utilizando
documentToHtmlString) - date (formateada utilizando el constructor
Date)
Finalmente, puedes usar las props de la página para mostrar tu publicación de blog.
---import { contentfulClient } from "../../lib/contentful";import { documentToHtmlString } from "@contentful/rich-text-html-renderer";import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() { const { items } = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost", }); const pages = items.map((item) => ({ params: { slug: item.fields.slug }, props: { title: item.fields.title, content: documentToHtmlString(item.fields.content), date: new Date(item.fields.date).toLocaleDateString(), }, })); return pages;}
const { content, title, date } = Astro.props;---<html lang="en"> <head> <title>{title}</title> </head> <body> <h1>{title}</h1> <time>{date}</time> <article set:html={content} /> </body></html>¡Navega a http://localhost:4321/ y haz clic en una de tus publicaciones para asegurarte de que tu ruta dinámica esté funcionando!
Renderizado bajo demanda
Sección titulada "Renderizado bajo demanda"Si has optado por el renderizado bajo demanda con un adaptador, utilizarás una ruta dinámica que usa un parámetro slug para obtener los datos de Contentful.
Crea una página [slug].astro en src/pages/posts. Utiliza Astro.params para obtener el slug de la URL, luego pásalo a getEntries:
---import { contentfulClient } from "../../lib/contentful";import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
const data = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost", "fields.slug": slug,});---Si no se encuentra la entrada, puedes redirigir al usuario a la página 404 utilizando Astro.redirect.
---import { contentfulClient } from "../../lib/contentful";import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
try { const data = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost", "fields.slug": slug, });} catch (error) { return Astro.redirect("/404");}---Para pasar los datos de la publicación a la sección de la plantilla, crea un objeto post fuera del bloque try/catch.
Utiliza documentToHtmlString para convertir el content de un Document a HTML, y utiliza el constructor Date para formatear la fecha. title se puede dejar tal cual. Luego, añade estas propiedades a tu objeto post.
---import Layout from "../../layouts/Layout.astro";import { contentfulClient } from "../../lib/contentful";import { documentToHtmlString } from "@contentful/rich-text-html-renderer";import type { BlogPost } from "../../lib/contentful";
let post;const { slug } = Astro.params;try { const data = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost", "fields.slug": slug, }); const { title, date, content } = data.items[0].fields; post = { title, date: new Date(date).toLocaleDateString(), content: documentToHtmlString(content), };} catch (error) { return Astro.redirect("/404");}---Finalmente, puedes hacer referencia a post para mostrar tu publicación de blog en la sección de la plantilla.
---import Layout from "../../layouts/Layout.astro";import { contentfulClient } from "../../lib/contentful";import { documentToHtmlString } from "@contentful/rich-text-html-renderer";import type { BlogPost } from "../../lib/contentful";
let post;const { slug } = Astro.params;try { const data = await contentfulClient.getEntries<BlogPost>({ content_type: "blogPost", "fields.slug": slug, }); const { title, date, content } = data.items[0].fields; post = { title, date: new Date(date).toLocaleDateString(), content: documentToHtmlString(content), };} catch (error) { return Astro.redirect("/404");}---<html lang="en"> <head> <title>{post?.title}</title> </head> <body> <h1>{post?.title}</h1> <time>{post?.date}</time> <article set:html={post?.content} /> </body></html>Publicando tu sitio
Sección titulada "Publicando tu sitio"Para desplegar tu sitio web, visita nuestras guías de despliegue y sigue las instrucciones para tu proveedor de alojamiento preferido.
Reconstruir al haber cambios en Contentful
Sección titulada “Reconstruir al haber cambios en Contentful”Si tu proyecto utiliza el modo estático por defecto de Astro, necesitarás configurar un webhook para activar una nueva compilación cuando tu contenido cambie. Si utilizas Netlify o Vercel como tu proveedor de alojamiento, puedes utilizar su función de webhook para activar una nueva compilación a partir de eventos de Contentful.
Netlify
Sección titulada "Netlify"Para configurar un webhook en Netlify:
-
Ve al panel de control de tu sitio y haz clic en Build & deploy.
-
En la pestaña Continuous Deployment, busca la sección Build hooks y haz clic en Add build hook.
-
Proporciona un nombre para tu webhook y selecciona la rama en la que deseas activar la compilación. Haz clic en Save y copia la URL generada.
Para configurar un webhook en Vercel:
-
Ve al panel de control de tu proyecto y haz clic en Settings.
-
En la pestaña Git, busca la sección Deploy Hooks.
-
Proporciona un nombre para tu webhook y la rama en la que deseas activar la compilación. Haz clic en Add y copia la URL generada.
Añadir un webhook a Contentful
Sección titulada “Añadir un webhook a Contentful”En la configuración de tu espacio de Contentful, haz clic en la pestaña Webhooks y crea un nuevo webhook haciendo clic en el botón Add Webhook. Proporciona un nombre para tu webhook y pega la URL del webhook que copiaste en la sección anterior. Finalmente, presiona Save para crear el webhook.
Ahora, cada vez que publiques una nueva publicación de blog en Contentful, se activará una nueva compilación y tu blog se actualizará.
Más guías de CMS
Socios de CMS destacados
-
CloudCannon
CMS basado en Git creado para mayor velocidad, seguridad y cero dolores de cabeza.