Saltar al contenido

Drupal & Astro

Drupal es una herramienta de gestión de contenidos de código abierto.

Para empezar, necesitarás lo siguiente:

  1. 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.

  2. Un sitio de Drupal - Si no has configurado un sitio de Drupal, puedes seguir las directrices oficiales de instalación de Drupal.

Para poder obtener contenido de Drupal, necesitas habilitar el módulo JSON:API de Drupal.

  1. Navega a la página de extensiones admin/modules a través del menú de administración Manage.
  2. Localiza el módulo JSON:API y marca la casilla que se encuentra a su lado
  3. Haz clic en Install para instalar el nuevo módulo

Ahora puedes realizar solicitudes GET a tu aplicación de Drupal a través de JSON:API.

Para añadir la URL de tu sitio de Drupal a Astro, crea un archivo .env en la raíz de tu proyecto (si no existe ya uno) y añade la siguiente variable:

.env
DRUPAL_BASE_URL="https://drupal.ddev.site/"

Reinicia el servidor de desarrollo para usar esta variable de entorno en tu proyecto de Astro.

Por defecto, el endpoint JSON:API de Drupal es accesible para solicitudes externas de obtención de datos sin necesidad de autenticación. Esto te permite obtener datos para tu proyecto de Astro sin credenciales, pero no permite que los usuarios modifiquen tus datos o la configuración del sitio.

Sin embargo, si deseas restringir el acceso y requerir autenticación, Drupal proporciona varios métodos de autenticación, incluyendo:

Puedes añadir tus credenciales a tu archivo .env.

.env
DRUPAL_BASIC_USERNAME="editor"
DRUPAL_BASIC_PASSWORD="editor"
DRUPAL_JWT_TOKEN="abc123"
...
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:

  • .env
  • astro.config.mjs
  • package.json

Las solicitudes y respuestas de JSON:API a menudo pueden ser complejas y estar profundamente anidadas. Para simplificar el trabajo con ellas, puedes usar dos paquetes de npm que agilizan tanto las solicitudes como la gestión de las respuestas:

  • JSONA: Serializador y deserializador de la especificación JSON API v1.0 para su uso en el servidor y en el navegador.
  • Drupal JSON-API Params: Este módulo proporciona una clase auxiliar para crear la consulta requerida. Al hacerlo, también intenta optimizar la consulta utilizando la forma corta siempre que sea posible.
Ventana de la terminal
npm install jsona drupal-jsonapi-params

Tu contenido se obtiene desde una URL de JSON:API.

La estructura básica de la URL es: /jsonapi/{entity_type_id}/{bundle_id}

La URL siempre está precedida por jsonapi.

  • El entity_type_id se refiere al tipo de entidad, como node, block, user, etc.
  • El bundle_id se refiere a los paquetes de entidad. En el caso de un tipo de entidad Node, el bundle podría ser article.
  • En este caso, para obtener la lista de todos los artículos, la URL será [DRUPAL_BASE_URL]/jsonapi/node/article.

Para recuperar una entidad individual, la estructura de la URL será /jsonapi/{entity_type_id}/{bundle_id}/{uuid}, donde uuid es el UUID de la entidad. Por ejemplo, la URL para obtener un artículo específico será de la forma /jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e.

Recupera solo ciertos campos añadiendo el campo Query String a la solicitud.

GET: /jsonapi/{entity_type_id}/{bundle_id}?field[entity_type]=field_list

Ejemplos:

  • /jsonapi/node/article?fields[node--article]=title,created
  • /jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e?fields[node--article]=title,created,body

Añade un filtro a tu solicitud agregando el filtro Query String.

El filtro más simple y común es un filtro de clave-valor:

GET: /jsonapi/{entity_type_id}/{bundle_id}?filter[field_name]=value&filter[field_other]=value

Ejemplos:

  • /jsonapi/node/article?filter[title]=Testing JSON:API&filter[status]=1
  • /jsonapi/node/article/2ee9f0ef-1b25-4bbe-a00f-8649c68b1f7e?fields[node--article]=title&filter[title]=Testing JSON:API

Puedes encontrar más opciones de consulta en la documentación de JSON:API.

Los componentes de Astro pueden obtener datos de tu sitio de Drupal utilizando el paquete drupal-jsonapi-params para construir la consulta.

El siguiente ejemplo muestra un componente con una consulta para un tipo de contenido “article” que tiene un campo de texto para un título y un campo de texto enriquecido para el contenido:

---
import {Jsona} from "jsona";
import {DrupalJsonApiParams} from "drupal-jsonapi-params";
import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
// Get the Drupal base URL
export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL;
// Generate the JSON:API Query. Get all title and body from published articles.
const params: DrupalJsonApiParams = new DrupalJsonApiParams();
params.addFields("node--article", [
"title",
"body",
])
.addFilter("status", "1");
// Generates the query string.
const path: string = params.getQueryString();
const url: string = baseUrl + '/jsonapi/node/article?' + path;
// Get the articles
const request: Response = await fetch(url);
const json: string | TJsonApiBody = await request.json();
// Initiate Jsona.
const dataFormatter: Jsona = new Jsona();
// Deserialise the response.
const articles = dataFormatter.deserialize(json);
---
<body>
{articles?.length ? articles.map((article: any) => (
<section>
<h2>{article.title}</h2>
<article set:html={article.body.value}></article>
</section>
)): <div><h1>No Content found</h1></div> }
</body>

Puedes encontrar más opciones de consulta en la documentación de JSON:API de Drupal

Con la configuración anterior, ahora puedes crear un blog que use Drupal como CMS.

  1. Un proyecto de Astro con JSONA y Drupal JSON-API Params instalados.

  2. Un sitio de Drupal con al menos una entrada - Para este tutorial, recomendamos comenzar con un nuevo sitio de Drupal con una instalación estándar.

    En la sección Content de tu sitio de Drupal, crea una nueva entrada haciendo clic en el botón Add. Luego, elige Article y completa los campos:

    • Title: My first article for Astro!
    • Alias: /articles/first-article-for astro
    • Descripción: ¡Este es mi primer artículo de Astro! ¡Veamos cómo se verá!

    Haz clic en Save para crear tu primer artículo. Siéntete libre de añadir tantos artículos como desees.

  1. Crea src/types.ts si aún no existe y añade dos nuevas interfaces llamadas DrupalNode y Path con el siguiente código. Estas interfaces coincidirán con los campos de tu tipo de contenido article en Drupal y los campos de Path. Las usarás para tipar la respuesta de tus entradas de artículos.

    src/types.ts
    export interface Path {
    alias: string
    pid: number
    langcode: string
    }
    export interface DrupalNode extends Record<string, any> {
    id: string
    type: string
    langcode: string
    status: boolean
    drupal_internal__nid: number
    drupal_internal__vid: number
    changed: string
    created: string
    title: string
    default_langcode: boolean
    sticky: boolean
    path: Path
    }

    Tu directorio src ahora debería incluir el nuevo archivo:

    • .env
    • astro.config.mjs
    • package.json
    • Directoriosrc/
      • types.ts
  2. Crea un nuevo archivo llamado drupal.ts bajo src/api y añade el siguiente código:

    src/api/drupal.ts
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {DrupalNode} from "../types.ts";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    // Get the Drupal Base Url.
    export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL;

    Esto importará las bibliotecas requeridas como Jsona para deserializar la respuesta, DrupalJsonApiParams para formatear la URL de la solicitud y los tipos Node y Jsona. También obtendrá la baseUrl del archivo .env.

    Tu directorio src/api ahora debería incluir el nuevo archivo:

    • .env
    • astro.config.mjs
    • package.json
    • Directoriosrc/
      • Directorioapi/
        • drupal.ts
      • types.ts
  3. En ese mismo archivo, crea la función fetchUrl para realizar la solicitud fetch y deserializar la respuesta.

    src/api/drupal.ts
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {DrupalNode} from "../types.ts";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    // Get the Drupal Base Url.
    export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL;
    /**
    * Fetch url from Drupal.
    *
    * @param url
    *
    * @return Promise<TJsonaModel | TJsonaModel[]> as Promise<any>
    */
    export const fetchUrl = async (url: string): Promise<any> => {
    const request: Response = await fetch(url);
    const json: string | TJsonApiBody = await request.json();
    const dataFormatter: Jsona = new Jsona();
    return dataFormatter.deserialize(json);
    }
  4. Crea la función getArticles() para obtener todos los artículos publicados.

    src/api/drupal.ts
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {DrupalNode} from "../types.ts";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    // Get the Drupal Base Url.
    export const baseUrl: string = import.meta.env.DRUPAL_BASE_URL;
    /**
    * Fetch url from Drupal.
    *
    * @param url
    *
    * @return Promise<TJsonaModel | TJsonaModel[]> as Promise<any>
    */
    export const fetchUrl = async (url: string): Promise<any> => {
    const request: Response = await fetch(url);
    const json: string | TJsonApiBody = await request.json();
    const dataFormatter: Jsona = new Jsona();
    return dataFormatter.deserialize(json);
    }
    /**
    * Get all published articles.
    *
    * @return Promise<DrupalNode[]>
    */
    export const getArticles = async (): Promise<DrupalNode[]> => {
    const params: DrupalJsonApiParams = new DrupalJsonApiParams();
    params
    .addFields("node--article", [
    "title",
    "path",
    "body",
    "created",
    ])
    .addFilter("status", "1");
    const path: string = params.getQueryString();
    return await fetchUrl(baseUrl + '/jsonapi/node/article?' + path);
    }

    Ahora puedes usar la función getArticles() en un componente .astro para obtener todos los artículos publicados con los datos de cada título, cuerpo, ruta y fecha de creación.

  5. Ve a la página de Astro donde obtendrás los datos de Drupal. El siguiente ejemplo crea una página de inicio de artículos en src/pages/articles/index.astro.

    Importa las dependencias necesarias y obtén todas las entradas de Drupal con un tipo de contenido de article usando getArticles() mientras pasas la interfaz DrupalNode para tipar tu respuesta.

    src/pages/articles/index.astro
    ---
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    import type { DrupalNode } from "../../types";
    import {getArticles} from "../../api/drupal";
    // Get all published articles.
    const articles = await getArticles();
    ---

    Esta llamada de obtención utilizando getArticles() devolverá un array tipado de entradas que se pueden utilizar en la plantilla de tu página.

    Tu directorio src/pages/ ahora debería incluir el nuevo archivo, si usaste el mismo archivo de página:

    • .env
    • astro.config.mjs
    • package.json
    • Directoriosrc/
      • Directorioapi/
        • drupal.ts
      • Directoriopages/
        • Directorioarticles/
          • index.astro
      • types.ts
  6. Añade contenido a tu página, como un título. Usa articles.map() para mostrar tus entradas de Drupal como elementos en una lista.

    src/pages/articles/index.astro
    ---
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    import type { DrupalNode } from "../types";
    import {getArticles} from "../api/drupal";
    // Get all published articles.
    const articles = await getArticles();
    ---
    <html lang="en">
    <head>
    <title>My news site</title>
    </head>
    <body>
    <h1>My news site</h1>
    <ul>
    {articles.map((article: DrupalNode) => (
    <li>
    <a href={article.path.alias.replace("internal:en/", "")}>
    <h2>{article.title}</h2>
    <p>Published on {article.created}</p>
    </a>
    </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 Drupal que se muestra arriba, pero esta vez, en una página que creará una ruta de página única para cada artículo.

Este ejemplo utiliza el modo estático predeterminado de Astro y crea un archivo de página de enrutamiento dinámico con la función getStaticPaths(). Esta función se llamará en el momento de la construcción para generar la lista de rutas que se convierten en páginas.

  1. Crea un nuevo archivo src/pages/articles/[path].astro e importa la interfaz DrupalNode y getArticle() de src/api/drupal.ts. Obtén tus datos dentro de una función getStaticPaths() para crear rutas para tu blog.

    src/pages/articles/[path].astro
    ---
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    import type { DrupalNode } from "../../types";
    import {getArticles} from "../../api/drupal";
    // Get all published articles.
    export async function getStaticPaths() {
    const articles = await getArticles();
    }
    ---

    Tu directorio src/pages/articles ahora debería incluir el nuevo archivo:

    • .env
    • astro.config.mjs
    • package.json
    • Directoriosrc/
      • Directorioapi/
        • drupal.ts
      • Directoriopages/
        • Directorioarticles/
          • index.astro
          • [path].astro
      • types.ts
  2. En el mismo archivo, mapea cada entrada de Drupal a un objeto con una propiedad params y otra props. La propiedad params se utilizará para generar la URL de la página y los valores de props se pasarán al componente de la página como propiedades (props).

    src/pages/articles/[path].astro
    ---
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    import type { DrupalNode } from "../../types";
    import {getArticles} from "../../api/drupal";
    // Get all published articles.
    export async function getStaticPaths() {
    const articles = await getArticles();
    return articles.map((article: DrupalNode) => {
    return {
    params: {
    // Choose `path` to match the `[path]` routing value
    path: article.path.alias.split('/')[2]
    },
    props: {
    title: article.title,
    body: article.body,
    date: new Date(article.created).toLocaleDateString('en-EN', {
    day: "numeric",
    month: "long",
    year: "numeric"
    })
    }
    }
    });
    }
    ---

    La propiedad dentro de params debe coincidir con el nombre de la ruta dinámica. Dado que el nombre del archivo es [path].astro, el nombre de la propiedad pasada a params debe ser path.

    En nuestro ejemplo, el objeto props pasa tres propiedades a la página:

    • title: una cadena de texto (string), que representa el título de tu publicación.
    • body: una cadena de texto (string), que representa el contenido de tu entrada.
    • date: una marca de tiempo (timestamp), basada en la fecha de creación de tu archivo.
  3. Usa las props de la página para mostrar tu publicación de blog.

    src/pages/articles/[path].astro
    ---
    import {Jsona} from "jsona";
    import {DrupalJsonApiParams} from "drupal-jsonapi-params";
    import type {TJsonApiBody} from "jsona/lib/JsonaTypes";
    import type { DrupalNode } from "../../types";
    import {getArticles} from "../../api/drupal";
    // Get all published articles.
    export async function getStaticPaths() {
    const articles = await getArticles();
    return articles.map((article: DrupalNode) => {
    return {
    params: {
    path: article.path.alias.split('/')[2]
    },
    props: {
    title: article.title,
    body: article.body,
    date: new Date(article.created).toLocaleDateString('en-EN', {
    day: "numeric",
    month: "long",
    year: "numeric"
    })
    }
    }
    });
    }
    const {title, date, body} = Astro.props;
    ---
    <html lang="en">
    <head>
    <title>{title}</title>
    </head>
    <body>
    <h1>{title}</h1>
    <time>{date}</time>
    <article set:html={body.value} />
    </body>
    </html>
  4. Navega a la vista previa del servidor de desarrollo y haz clic en una de tus publicaciones para asegurarte de que tu ruta dinámica esté funcionando.

Para desplegar tu sitio web, visita nuestras guías de despliegue y sigue las instrucciones para tu proveedor de alojamiento preferido.

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.

Todas las guías de CMS

Contribuir Comunidad Patrocinar