Saltar al contenido

El frontmatter: código que nunca llega al cliente

El component script de un .astro por dentro: cuándo corre —en el build para rutas estáticas, por petición en las dinámicas—, cómo escribir imports, variables y fetch con await de nivel superior, y por qué ese código jamás se envía al navegador del visitante.

Cuándo corre la valla

El frontmatter no se ejecuta cuando el visitante abre la página: se ejecuta antes, del lado del constructor. El cuándo exacto depende del output del proyecto que viste en el nivel anterior. Con output: 'static', la valla corre una sola vez durante astro build y su resultado queda congelado en un .html. Con output: 'server' o en una ruta marcada como dinámica, la valla corre de nuevo en cada petición, dentro del servidor, para componer una respuesta fresca.

En ambos casos el rasgo invariante es el mismo: la valla corre en una máquina que no es el navegador del visitante. Tu build en CI o tu servidor de producción ejecutan ese código; el cliente solo recibe el HTML que produjo. Esa distancia es la que hace posible todo lo demás.

Esta distinción de tiempos tiene una consecuencia que conviene fijar pronto: en modo estático, si los datos que pides cambian en su origen después del build, tu página no se entera hasta la próxima construcción. En modo servidor, cada visita ve datos frescos, a cambio de ejecutar la valla una y otra vez. Elegir dónde corre la valla es elegir entre frescura y coste por petición.

ASTRO
---// en un proyecto server, congela ESTA ruta en el buildexport const prerender = true;const paginas = await cargarPaginasEstaticas();---

Imports y variables: un módulo de verdad

Dentro de la valla escribes JavaScript o TypeScript sin restricciones de dialecto. Importas componentes para componerlos, utilidades para reutilizar lógica y funciones del propio Astro como getCollection. Declaras variables con const y let, calculas, transformas y ordenas datos. Todo lo que sea válido en un módulo ES lo es aquí.

ASTRO
---import Tarjeta from '../components/Tarjeta.astro';import { getCollection } from 'astro:content';import { formatearFecha } from '../lib/fechas.ts';const posts = await getCollection('blog');const recientes = posts  .sort((a, b) => b.data.fecha - a.data.fecha)  .slice(0, 3);---

Fíjate en el await de la primera línea de lógica: el frontmatter admite await de nivel superior sin envolverlo en una función asíncrona. Esto es posible porque Astro trata la valla entera como un cuerpo asíncrono, y es lo que permite pedir datos de forma lineal, sin promesas anidadas ni callbacks. Lees el código de arriba abajo como una receta: trae, ordena, recorta.

En la valla puedes importar casi cualquier cosa: otros componentes .astro, componentes de framework, funciones utilitarias en .ts, datos en JSON, e incluso módulos de Node para leer el sistema de archivos cuando corres en el servidor. El único límite lo marca el destino de ejecución: un módulo que dependa del navegador no tiene sentido aquí, porque aquí no hay navegador.

ASTRO
---import datos from '../data/menu.json';import { calcularTotal } from '../lib/carrito.ts';const total = calcularTotal(datos);---<p>Total del menu: {total}</p>

fetch en el frontmatter

Como la valla corre en el servidor, fetch es de primera clase: pides datos a una API, lees un archivo, consultas una base de datos, y el resultado se materializa en HTML antes de llegar al visitante. No hay estado de carga que gestionar en el cliente, ni parpadeo de contenido: cuando la página aparece, los datos ya están dentro.

ASTRO
---const clave = import.meta.env.API_KEY;const respuesta = await fetch('https://api.ejemplo.com/productos', {  headers: { Authorization: `Bearer ${clave}` },});const productos = await respuesta.json();---<ul>  {productos.map((p) => <li>{p.nombre}</li>)}</ul>

Observa el uso de import.meta.env.API_KEY. Una variable de entorno sin el prefijo público solo existe en el servidor: la usas para autenticar la petición y nunca aparece en el HTML. El visitante ve la lista de productos, jamás la clave que hizo falta para obtenerla. Ese secreto vivió y murió en la valla.

Cuando necesitas varias fuentes a la vez, el await de nivel superior se combina con las herramientas normales de concurrencia. Promise.all lanza las peticiones en paralelo y espera a todas, en lugar de encadenar esperas que sumarían sus tiempos:

ASTRO
---const [usuarios, articulos] = await Promise.all([  fetch('https://api.ejemplo.com/usuarios').then((r) => r.json()),  fetch('https://api.ejemplo.com/articulos').then((r) => r.json()),]);---<p>{usuarios.length} usuarios y {articulos.length} articulos</p>

Que esto ocurra en el servidor aporta una ventaja de red que se subestima: las llamadas parten desde tu infraestructura, no desde el dispositivo del visitante, que suele tener peor latencia y ancho de banda. El visitante recibe el resultado ya montado, sin sufrir la cascada de peticiones que un cliente tendría que hacer.

La frontera que el código no cruza

La plantilla consume los valores de la valla, pero el código de la valla no cruza a la salida. Astro evalúa el frontmatter, retiene los valores que la plantilla necesita y descarta todo lo demás: los imports, las llamadas a la red, los bucles intermedios. Lo que el navegador descarga es el resultado, nunca la maquinaria.

Comprobar esto es trivial y muy formativo: construye el sitio, abre el HTML generado con un editor de texto y busca en él el nombre de una variable de tu valla o la URL de tu fetch. No los encontrarás. Solo está el resultado —el texto, la lista, los números— sin ninguna huella del código que lo produjo. Esa ausencia es, literalmente, la seguridad y la ligereza de Astro hechas archivo.