Data Fetching y APIs en Astro
Laboratorio: cuándo se leen los datos #
Actualiza una fuente ficticia y compara visitas en SSG, SSR y obtención de datos en el cliente. Todas las etapas se simulan localmente, sin peticiones reales.
Laboratorio interactivo
SSG, SSR y datos en el cliente
Compara qué versión de un precio ve cada visitante. Fuente, build, servidor y fetch son modelos en memoria: aquí no se realiza ninguna petición ni se utiliza backend.
Fuente ficticia: 20 € · versión 1
HTML del último build: 20 € · versión 1
SSG
Sin visita
—
HTML listo desde el build.
SSR
Sin visita
—
HTML generado para esta visita.
Cliente (CSR)
Sin visita
—
HTML shell; datos después del fetch.
Visitas: 0. Solicita una página para ver los tres modos con la misma fuente.
Sin cachés, revalidación ni streaming. SSR en un proyecto Astro real requiere el entorno/adaptador correspondiente; un componente hidratado puede usar HTML de SSG o SSR. CSR aquí significa obtener los datos de esta tarjeta en el cliente. Renderizado bajo demanda en Astro.
Experimenta: Solicita la página, cambia la fuente y solicita otra vez. SSG conserva la versión del build; SSR lee la nueva. El cliente solo obtiene datos al pulsar su fetch. Reconstruye y compara.
Astro es un framework estático por naturaleza, pero te permite traer datos en tiempo de construcción o en tiempo real, lo que lo hace ideal tanto para webs informativas como para aplicaciones dinámicas.
1. ¿Qué significa data fetching? #
Data fetching = “obtener datos”.
Puedes traer datos de:
- Archivos JSON o locales.
- APIs externas (por ejemplo, el clima, usuarios, etc.).
- Bases de datos (con adaptadores o funciones server-side).
💡 En Astro, normalmente se obtienen en el servidor (dentro de los ---) antes de renderizar la página.
2. Obtener datos desde un archivo local (JSON) #
Supón que tienes un archivo:
src/data/cursos.json
1[ 2 { "id": 1, "nombre": "HTML y CSS", "nivel": "Básico" }, 3 { "id": 2, "nombre": "JavaScript", "nivel": "Intermedio" }, 4 { "id": 3, "nombre": "Astro", "nivel": "Avanzado" } 5]
Puedes leerlo en una página Astro así:
1--- 2import cursos from '../data/cursos.json'; 3--- 4 5<h1>Cursos disponibles</h1> 6<ul> 7 {cursos.map(curso => ( 8 <li> 9 {curso.nombre} — <em>{curso.nivel}</em> 10 </li> 11 ))} 12</ul>
✅ Se renderiza todo en el servidor.
✅ El usuario solo recibe HTML (sin necesidad de JavaScript extra).
3. Obtener datos desde una API externa #
Puedes usar fetch() directamente en la parte superior del archivo .astro:
1--- 2const respuesta = await fetch("https://jsonplaceholder.typicode.com/users"); 3const usuarios = await respuesta.json(); 4--- 5 6<h1>Lista de usuarios</h1> 7<ul> 8 {usuarios.map(u => <li>{u.name} — {u.email}</li>)} 9</ul>
💡 Esto se ejecuta una vez durante la compilación (en astro build),
por lo que los datos se guardan en el HTML final (rendimiento máximo 🚀).
4. Obtener datos en tiempo real (server-side rendering) #
Astro también puede obtener datos cada vez que alguien visita la página, en lugar de precompilarla.
Esto se llama SSR (Server-Side Rendering).
Paso 1. Instalar el adaptador y activar SSR #
Para practicar con un servidor Node:
1npx astro add node
Edita astro.config.mjs y conserva las integraciones o plugins que ya uses:
1import { defineConfig } from "astro/config"; 2import node from "@astrojs/node"; 3 4export default defineConfig({ 5 adapter: node({ mode: "standalone" }), 6 security: { allowedDomains: [{ hostname: 'localhost' }, { hostname: '127.0.0.1' }] }, 7 output: "server", 8});
Al publicar configura también el hostname real del sitio en security.allowedDomains.
Paso 2. Crear una página SSR #
1--- 2const res = await fetch('https://api.coinpaprika.com/v1/tickers/btc-bitcoin'); 3if (!res.ok) { 4 throw new Error(`No se pudo obtener el precio de Bitcoin (${res.status})`); 5} 6const data = await res.json(); 7const precio = new Intl.NumberFormat('es-ES', { 8 style: 'currency', 9 currency: 'USD', 10}).format(data.quotes.USD.price); 11--- 12 13<h1>Precio actual del Bitcoin</h1> 14<p>1 BTC = {precio}</p>
Coinpaprika devuelve el precio en dólares estadounidenses en quotes.USD.price. Usamos Intl.NumberFormat para mostrarlo con formato de moneda.
💡 Con el adaptador y output: 'server', cada visita ejecuta esta página. Si quieres mantener el resto del sitio estático, conserva el output predeterminado y añade export const prerender = false solo a la página o endpoint dinámico. output: 'hybrid' no es un modo actual.
Después de compilar con npm run build, el adaptador Node standalone se inicia con node ./dist/server/entry.mjs. Vercel, Netlify o Cloudflare Workers requieren su propio adaptador.
✅ Ideal para datos cambiantes (clima, precios, noticias…).
❌ Menos rápido que una página estática (ya que consulta el servidor cada vez).
5. Crear páginas dinámicas con datos externos #
Imagina que quieres crear una página por cada usuario que viene de una API.
Paso 1. Crear una página dinámica #
src/pages/users/[id].astro
1--- 2// 1. En modo estático (SSG por defecto), genera las rutas en el build: 3export async function getStaticPaths() { 4 const res = await fetch("https://jsonplaceholder.typicode.com/users"); 5 const users = await res.json(); 6 return users.map((user) => ({ 7 params: { id: user.id.toString() }, 8 props: { user }, 9 })); 10} 11 12// 2. Recibe los datos precompilados de cada usuario 13const { user } = Astro.props; 14--- 15 16<h1>{user.name}</h1> 17<p>Email: {user.email}</p> 18<p>Ciudad: {user.address.city}</p>
💡 (Si tuvieras activado SSR con output: 'server' o export const prerender = false, no necesitas getStaticPaths y puedes leer const { id } = Astro.params directamente en cada petición).
Paso 2. Crear enlaces a cada usuario #
src/pages/users/index.astro
1--- 2const res = await fetch("https://jsonplaceholder.typicode.com/users"); 3const users = await res.json(); 4--- 5 6<h1>Usuarios</h1> 7<ul> 8 {users.map(u => <li><a href={`/users/${u.id}`}>{u.name}</a></li>)} 9</ul>
🟢 Cada usuario tendrá su propia página dinámica con datos reales.
6. Usar endpoints personalizados (rutas API) #
Astro también puede actuar como un mini backend.
Puedes crear rutas API que devuelvan datos en formato JSON.
Ejemplo:
src/pages/api/hora.js
1export const prerender = false; 2 3export async function GET() { 4 return new Response( 5 JSON.stringify({ hora: new Date().toLocaleTimeString() }), 6 { headers: { "Content-Type": "application/json" } } 7 ); 8}
Este endpoint necesita el adaptador instalado en el paso anterior. Al desactivar su prerenderizado, la hora se calcula durante cada petición. Sin esa opción en un proyecto estático, se congelaría durante el build.
Y puedes acceder desde el navegador:
👉 /api/hora
Resultado:
1{ "hora": "15:42:09" }
💡 Muy útil para:
- Probar funciones backend.
- Crear APIs personalizadas.
- Servir datos a otros sitios o apps.
7. Ejemplo completo: Mostrar clima real #
1--- 2const ciudad = "Madrid"; 3const res = await fetch(`https://wttr.in/${ciudad}?format=j1`); 4const data = await res.json(); 5const temp = data.current_condition[0].temp_C; 6const desc = data.current_condition[0].weatherDesc[0].value; 7--- 8 9<h1>El tiempo en {ciudad}</h1> 10<p>Temperatura: {temp} °C</p> 11<p>Condición: {desc}</p>
Este fragmento obtiene datos cuando se ejecuta la página: durante el build si es estática, o por petición si utiliza SSR con adaptador. Para mostrar el clima actualizado en cada visita, añade export const prerender = false al frontmatter y conserva el adaptador.
🟣 Puedes cambiar “Madrid” por cualquier ciudad.
8. Cuándo usar cada tipo de data fetching #
| Tipo de dato | Cuándo usarlo | Ejemplo |
|---|---|---|
| Local (JSON, Markdown) | Datos estáticos que no cambian | Blog, productos fijos |
| API externa (estático) | Datos externos pero poco cambiantes | Listado de usuarios |
| SSR (tiempo real) | Datos que cambian constantemente | Clima, precios, cotizaciones |
| Rutas API | Cuando quieres crear tu propio backend | /api/datos |
9. Islas de servidor: una parte dinámica en una página estática #
Las islas de cliente (client:*) añaden interactividad en el navegador. Una isla de servidor (server:defer) consulta o renderiza una parte del contenido en el servidor sin bloquear el HTML de la página principal. Requiere adaptador.
📄 src/components/ServerTime.astro
1--- 2const time = new Date().toLocaleTimeString('es-ES'); 3--- 4<p>Hora calculada en el servidor: {time}</p>
En una página que importe ServerTime desde la ruta correspondiente:
1<ServerTime server:defer> 2 <p slot="fallback">Consultando la hora…</p> 3</ServerTime>
La página puede estar prerenderizada, pero esa sección se solicita al servidor. No pases secretos como props a una isla: se transmiten para poder resolver su solicitud. Para contenido personalizado comprueba la sesión en el componente. Islas de servidor.
Resumen del tema
Conceptos clave #
- Fetch en tiempo de compilación (SSG por defecto): llamadas asíncronas con
await fetch()en el frontmatter que se ejecutan duranteastro build, inyectando los datos directamente en el HTML final. - Server-Side Rendering (SSR): activación con un adaptador y
output: 'server', o por ruta conprerender = falsepara consultar APIs y bases de datos en tiempo real en cada petición. - Carga de datos locales: importación directa de archivos JSON (
import datos from '../data/datos.json') procesados en el servidor sin transferir sobrecarga al cliente. - Endpoints y Rutas API (
src/pages/api/): creación de funciones serverless exportando métodos HTTP (GET,POST,PUT,DELETE) que devuelven objetos estándarResponse(JSON.stringify(...)). - Rutas parametrizadas dinámicas: consumo de parámetros mediante
Astro.paramspara filtrar y renderizar vistas de detalle basadas en IDs o slugs.
Qué debes recordar #
En Astro, las llamadas a fetch() en el frontmatter se ejecutan en el servidor/build sin enviar JavaScript al cliente; usa endpoints API (GET/POST) para construir servicios backend ligeros.