wandres.dev
ADAPTERS · Node, Cloudflare, Vercel

Qué es un adapter y por qué SSR lo necesita

La pieza que falta cuando un sitio deja de ser estático: el adapter como interfaz entre el motor de Astro y un runtime concreto. Por qué el modo estático no lo necesita y el bajo demanda sí, cómo el par Request/Response de la plataforma web es el contrato compartido, cómo se instala y registra con astro add, y cómo estático y dinámico conviven en un mismo build.

⏱ 14 min

El modo estático de Astro no necesita servidor: el build emite HTML, CSS y JavaScript, ficheros planos que cualquier CDN sirve sin ejecutar una línea de tu código. Pero en cuanto una sola ruta se renderiza bajo demanda, alguien tiene que correr tu código en el instante de cada petición —y Astro, por sí solo, no sabe hablar con Vercel, con Cloudflare ni con tu servidor Node—. El adapter es esa pieza de traducción: enseña a Astro a construir para un runtime concreto y a convertir la petición nativa de esa plataforma en el Request estándar que su motor entiende. Sin adapter no hay bajo demanda; con él, el mismo proyecto aterriza en destinos radicalmente distintos.

🎯 Al terminar esta lección sabrás
  • Entender por qué el modo estático no necesita adapter y el bajo demanda sí.
  • Ver el adapter como la interfaz entre el motor de Astro y un runtime concreto.
  • Reconocer el par Request/Response de la plataforma web como el contrato compartido.
  • Instalar y registrar un adapter con astro add, y ver qué produce el build.

Sin servidor no hay quién ejecute tu código

Un sitio estático es un montón de ficheros terminados. El build los cuece una vez, y a partir de ahí servirlos es un acto mecánico: un CDN o un cubo de almacenamiento devuelve bytes que ya existían, sin computar nada. Por eso el modo static no pide adapter —no hay código tuyo corriendo en producción, solo artefactos horneados—.

El renderizado bajo demanda rompe esa foto. Una ruta con export const prerender = false no se resuelve en el build: se resuelve cuando llega la petición, leyendo Astro.params, consultando datos frescos, decidiendo la respuesta al vuelo. Eso exige un proceso vivo que ejecute tu código en cada visita. Astro sabe producir ese código —su build genera un manejador de peticiones—, pero no sabe, por sí mismo, cómo la plataforma de destino lo arranca ni con qué forma le entrega cada petición. Ese saber es justo lo que aporta el adapter.

⚠️
Sin adapter, el build falla en cuanto algo es dinámico

Si marcas una ruta como bajo demanda y ejecutas el build sin un adapter instalado, Astro se detiene con un error explícito: no puede construir para un destino que no conoce. No es un fallo tuyo de código, es una pieza que falta en la configuración. El adapter es el requisito estructural del SSR, no un extra opcional que se pueda posponer.

El adapter: un traductor entre dos formas de petición

El motor de Astro está escrito contra la plataforma web. Su corazón es una aplicación que recibe un Request estándar y devuelve un Response estándar —los mismos objetos que existen en cualquier navegador—. Esa firma es universal y no menciona a ningún proveedor.

// La idea del contrato interno, no codigo que escribas a diario
const app = new App(manifest);
const response = await app.render(request); // Request -> Response

El problema es que ningún runtime real habla exactamente ese dialecto. Node entrega un IncomingMessage y espera que escribas en un ServerResponse. Una función serverless recibe un objeto de evento con su propia forma. Un Worker de Cloudflare ya trae una petición casi estándar, pero con extras propios. El adapter es el traductor bidireccional: toma la petición nativa de la plataforma, la convierte en el Request que el motor espera, y transforma el Response de vuelta en lo que el runtime sabe emitir.

Esa es la razón por la que nunca escribes manejo de peticiones específico de una plataforma dentro de tus páginas. No parseas cabeceras al estilo de Node ni construyes la salida con la API de un proveedor: lees Astro.request, devuelves un Response, y confías en que el adapter haga la conversión en ambos sentidos. Toda la especificidad del runtime empieza y termina en esa capa delgada.

ℹ️
Un adapter es una integración especial

Técnicamente, un adapter es una integración de Astro con un rol reservado: además de engancharse a los hooks del ciclo de vida, declara el runtime de destino, el formato de salida del build y qué características de servidor soporta. Por eso no vive en el array integrations, sino en la clave adapter de la configuración: solo puede haber uno, porque un build apunta a un único destino a la vez.

Instalar y registrar un adapter

La forma canónica de añadir un adapter es astro add, que instala el paquete, lo registra en astro.config.mjs y ajusta lo que haga falta sin que edites nada a mano.

# Elige el destino: node, vercel, netlify o cloudflare
npx astro add node
# instala @astrojs/node, importa el paquete y rellena la clave adapter

El resultado en la configuración es una sola línea nueva, pero es la que cambia todo el destino del proyecto:

import { defineConfig } from 'astro/config';
import node from '@astrojs/node';

export default defineConfig({
  adapter: node({ mode: 'standalone' }),
});

Con el adapter presente, las rutas bajo demanda ya tienen dónde ejecutarse. Fíjate en lo que no ha cambiado: ni una sola de tus páginas. El acoplamiento a la plataforma cabe entero en esa importación y esa clave; el resto de tu código sigue hablando el lenguaje universal del motor. Astro mantiene un puñado de adapters oficiales, uno por familia de destino.

🖥️

@astrojs/node

Un servidor Node que tú controlas, en modo standalone o como middleware de tu propio Express.

@astrojs/vercel

Funciones serverless o de edge en Vercel, con renderizado incremental e imágenes de la plataforma.

🌐

@astrojs/netlify

Funciones y edge functions de Netlify, con su CDN de imágenes y despliegue integrado.

☁️

@astrojs/cloudflare

Workers en el edge sobre APIs web, con acceso a KV, D1, R2 y geolocalización.

Estático y dinámico conviven en el mismo build

Con un adapter presente no estás obligado a volver dinámico todo el sitio. El output puede seguir siendo static y tú marcas bajo demanda solo las rutas que lo necesiten con export const prerender = false. El build entiende esa mezcla y produce dos artefactos complementarios.

dist/
  client/   # HTML prerenderizado y assets, servibles por un CDN
  server/   # el handler que ejecuta las rutas bajo demanda

La parte de client son ficheros terminados que la plataforma sirve sin computar nada; la de server es el código que el adapter conecta al runtime para las rutas vivas. El adapter no solo traduce peticiones: también organiza este reparto, indicándole a la plataforma qué servir como estático y qué enviar a tu manejador.

---
// src/pages/blog/[slug].astro  ->  estatico por defecto
// src/pages/carrito.astro      ->  bajo demanda
export const prerender = false;
---

El mismo mecanismo funciona al revés. Si configuras output: 'server', todo se vuelve bajo demanda por defecto y marcas con export const prerender = true las páginas que sí quieras hornear —la portada, el aviso legal—. Estático y servidor no son dos proyectos distintos, sino dos posiciones por defecto del mismo dial, y el adapter sostiene cualquiera de ellas sin que cambie nada más en tu código.

💡
El adapter habilita lo dinámico, no obliga a ello

Instalar un adapter no convierte tu sitio en una aplicación de servidor: solo abre la posibilidad. Un blog puede seguir siendo casi todo estático —rápido, cacheable, barato— y reservar el renderizado bajo demanda para la búsqueda, el carrito o un panel. Esa mezcla es la norma, no la excepción, y el adapter la sostiene sin obligarte a elegir un extremo para todo el proyecto.

📝
astro add hace más que instalar

Cuando ejecutas astro add, el comando no solo baja el paquete: detecta lo que tu proyecto necesita, escribe la importación, rellena la clave adapter y, si hace falta, ajusta el output. Editar todo eso a mano es posible, pero astro add lo hace sin errores de sintaxis y deja la configuración en un estado coherente desde el primer momento.

flowchart LR
PLAT[peticion nativa de la plataforma] --> AD[adapter traduce de entrada]
AD --> REQ[Request web estandar]
REQ --> APP[motor App de Astro]
APP --> RES[Response web estandar]
RES --> AD2[adapter traduce de salida]
AD2 --> OUT[respuesta nativa de la plataforma]
style APP fill:#89b4fa,color:#11111b
style RES fill:#a6e3a1,color:#11111b
El adapter existe porque Astro apostó por los estándares del navegador

Que baste cambiar una línea para mudar tu sitio de tu portátil a la red global de un proveedor no es magia ni casualidad: es la consecuencia visible de una decisión de arquitectura tomada mucho antes, en el corazón del motor de Astro. Ese motor no está escrito contra Node, ni contra ninguna plataforma; está escrito contra la plataforma web —los mismos objetos Request, Response, URL, Headers y fetch que existen en cualquier navegador—. Una página de Astro, en el fondo, es una función que recibe un Request y devuelve un Response, y esa firma no nombra a ningún proveedor. El adapter es el fino traductor que conecta esa función universal con el dialecto concreto de un runtime: convierte el IncomingMessage de Node, o el evento de una función serverless, o la petición de un Worker, en el Request estándar que el motor espera, y hace el viaje de vuelta con el Response. Toda la lógica de tu sitio vive del lado universal; toda la dependencia del proveedor vive comprimida en el adapter, una capa delgada y reemplazable que no toca tu código. Esta es una lección de diseño que trasciende Astro: cuando programas contra un estándar y aíslas lo específico del proveedor en una frontera nombrada, compras portabilidad de verdad —no la portabilidad prometida de un framework que te encierra, sino la que se demuestra el día que cambias node por cloudflare y todo sigue funcionando—. El adapter no es un detalle de despliegue: es la prueba de que el acoplamiento a la plataforma puede ser una elección tardía y localizada, en lugar de una condena que impregna cada fichero.

⚔️ Provoca y resuelve la exigencia del adapter
  1. En un proyecto estático, marca una ruta con export const prerender = false y ejecuta el build: lee con calma el error que reclama un adapter.
  2. Instala uno con npx astro add node y observa exactamente qué línea aparece en astro.config.mjs.
  3. Vuelve a construir, localiza en dist las carpetas client y server, y razona qué papel cumple cada una.
  4. Explica con tus palabras por qué el mismo motor de Astro puede servir tanto en Node como en el edge sin reescribir tus páginas.