wandres.dev
ADAPTERS · Node, Cloudflare, Vercel

Elegir plataforma y portar entre adapters

La decisión de destino como compromiso de ingeniería, no de preferencia: modelos de coste (servidor fijo, serverless por invocación, edge por petición), cold starts, límites de CPU y memoria por runtime, y compatibilidad de APIs. Cómo confinar lo específico del proveedor tras una frontera propia, y por qué, gracias a que Astro habla estándares web, portar entre adapters puede ser un cambio de una línea si no te ataste al runtime.

⏱ 16 min

Con varios adapters que “simplemente funcionan”, la pregunta real ya no es cómo desplegar, sino dónde —y la respuesta es un compromiso de ingeniería, no un gusto—. El modelo de coste (un servidor fijo, pago por invocación, pago por petición), los cold starts, los límites de tiempo de CPU y de memoria de cada runtime, y la compatibilidad de APIs tiran en direcciones distintas, y ninguna opción gana en todos los ejes a la vez. La buena noticia es que, como el motor de Astro habla estándares web, portar de un adapter a otro puede ser un cambio de una sola línea —siempre que no te hayas atado a las APIs privadas de un runtime concreto—. Elegir plataforma, y conservar la libertad de cambiarla, es una de las decisiones de arquitectura más rentables que tomarás.

🎯 Al terminar esta lección sabrás
  • Comparar modelos de coste: servidor fijo, serverless por invocación y edge por petición.
  • Sopesar cold starts y límites de CPU y memoria según el runtime.
  • Evaluar compatibilidad: APIs de Node frente al runtime web del edge.
  • Confinar lo específico del proveedor para portar entre adapters con poco coste.

Coste, cold starts y límites de recursos

Los tres modelos de despliegue no son variantes de lo mismo: son economías distintas. Un servidor Node propio tiene coste fijo —pagas la máquina esté ociosa o saturada— y a cambio no sufre cold starts y te da toda la CPU y memoria del equipo. El serverless cobra por invocación, escala a cero cuando nadie visita, pero paga el arranque en frío tras la inactividad y acota memoria y tiempo por función. El edge cobra por petición, arranca casi instantáneamente y llega cerca del usuario, a cambio de límites de tiempo de CPU y de memoria mucho más estrechos.

🖥️

Servidor Node fijo

Coste constante, sin cold start, CPU y memoria de la máquina entera. Tú operas, parcheas y escalas a mano.

🟩

Serverless

Pago por invocación, escala a cero, cero operación. Cold start tras inactividad y límites de memoria y tiempo.

Edge

Pago por petición, cold start casi nulo, latencia global mínima. Tiempo de CPU y memoria muy acotados.

🧠

El eje del recurso

Más estado y CPU disponible tira hacia Node; menos latencia y cero operación tira hacia el edge.

Traducido a decisiones: una carga con picos impredecibles y valles largos favorece el serverless, que no paga cuando nadie llama. Un tráfico alto y constante puede salir más barato en un servidor fijo, donde el coste por petición se diluye. Una audiencia global que exige respuestas rápidas en todas partes pide edge. Y una carga que necesita mucha CPU sostenida por petición choca contra los límites del edge y vuelve a Node. El eje que ordena todo es cuánto trabajo y estado necesitas por petición frente a cuánta cercanía y elasticidad.

📝
El cold start no es un número abstracto

Un arranque en frío de cientos de milisegundos no se nota en un cron ni en una API interna, pero sí en la primera visita a una página poco transitada, donde el usuario espera mirando una pantalla en blanco. Si tu tráfico tiene muchas rutas frías y raras, el edge —con su arranque casi nulo— o un servidor siempre encendido protegen esa primera impresión mejor que el serverless. Mide el cold start donde el usuario lo siente, en la cola de peticiones lentas, no en el promedio que lo disimula.

Compatibilidad: qué puede correr dónde

El coste orienta, pero la compatibilidad manda: de nada sirve la plataforma más barata si tu código no corre en ella. Un proyecto que usa un módulo nativo, lee del sistema de ficheros o consume mucha CPU necesita el runtime Node —servidor propio o serverless—. Un proyecto de manejadores ligeros que solo usa APIs web puede vivir en el edge y disfrutar su latencia. Entre ambos, mucho contenido con puntos dinámicos sencillos encaja cómodo en serverless.

💡
Deja que la incompatibilidad recorte el mapa primero

Antes de comparar precios, comprueba qué runtime admite tu código. Si dependes de Sharp para procesar imágenes bajo demanda, de un cliente con binario nativo o de fs, el edge queda descartado sin más debate, y la decisión se reduce a servidor propio frente a serverless. La compatibilidad es una restricción dura; el coste, una optimización dentro de lo que la restricción permite. Ordenar así las preguntas evita enamorarte de una plataforma en la que tu proyecto no cabe.

Portar entre adapters

Aquí se cobra la apuesta de Astro por los estándares. Cambiar de destino es, en el mejor caso, sustituir la importación y la clave adapter, ajustar el fichero de despliegue del proveedor y volver a construir. Tus páginas no se tocan, porque siguen hablando Request y Response.

// de un servidor Node...
import node from '@astrojs/node';
export default defineConfig({ adapter: node({ mode: 'standalone' }) });

// ...al edge de Cloudflare, en una linea
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({ adapter: cloudflare() });

Lo que complica un port no es Astro, sino lo que tú añadiste por encima: un fs o un binario nativo que el edge rechaza, un Astro.locals.runtime.env de Cloudflare que no existe en Node, un servicio de imágenes específico. La portabilidad, por tanto, no es suerte: es una consecuencia de cómo escribes.

Cada proveedor añade, además, su propia envoltura de despliegue: un vercel.json, un netlify.toml, un wrangler.jsonc con sus bindings. El adapter genera casi todo eso por ti, pero portar implica revisarlo: dónde se declaran las variables de entorno, cómo se enrutan los estáticos, qué comando de build espera la plataforma. Es trabajo de configuración, no de código, y por eso es acotado, visible y repetible —muy distinto de reescribir la lógica de tu sitio—.

La frontera del proveedor: un patrón para no atarte

La técnica que mantiene barato el port es poner en cuarentena cada llamada específica del proveedor tras un módulo tuyo. En vez de esparcir Astro.locals.runtime.env por decenas de páginas, escribes una sola pieza que expone una interfaz neutral y esconde qué plataforma la respalda. Portar toca entonces ese único fichero, no todo el proyecto.

// src/lib/storage.ts  ->  la unica puerta al almacenamiento del proveedor
export interface Store {
  get(clave: string): Promise<string | null>;
  set(clave: string, valor: string): Promise<void>;
}

// implementacion respaldada por un binding KV de Cloudflare
export function storeDesde(locals: App.Locals): Store {
  const kv = locals.runtime.env.MI_KV;
  return {
    get: (clave) => kv.get(clave),
    set: (clave, valor) => kv.put(clave, valor),
  };
}

Tus páginas dependen de Store, no de Cloudflare: piden un valor y lo guardan sin saber quién hay debajo. Cuando llega el día de portar, la nueva implementación vive en el mismo fichero y respeta la misma interfaz.

// la misma interfaz Store, ahora sobre Redis en un servidor Node
import { createClient } from 'redis';
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();

export function storeDesde(): Store {
  return {
    get: (clave) => redis.get(clave),
    set: async (clave, valor) => { await redis.set(clave, valor); },
  };
}

Ni una sola página cambia: todas siguen pidiendo un Store, y solo esta función sabe que ahora hay Redis y no un binding KV debajo. Ese es el retorno concreto de la disciplina: el acoplamiento existe, pero vive encerrado donde puedes verlo y cambiarlo de golpe.

El equilibrio tiene otra cara que conviene decir en voz alta: confinar no es renunciar. Si Cloudflare resuelve tu geolocalización o tu almacenamiento mejor que cualquier alternativa portable, úsalo sin culpa —el objetivo no es el mínimo común denominador que empobrece tu sitio, sino saber con exactitud qué te ata y poder soltarlo el día que convenga—. Una abstracción bien puesta te deja explotar lo mejor de una plataforma hoy sin firmar que te quedarás en ella para siempre; es libertad con la puerta abierta, no ascetismo tecnológico.

📝
La abstracción no es sobreingeniería cuando confina un riesgo real

Envolver el runtime tras una interfaz puede parecer ceremonia innecesaria, y lo sería si no protegiera algo valioso. Aquí protege tu movilidad: la diferencia entre un port de una tarde y uno de semanas. La regla sensata es abstraer solo lo que de verdad te ata —el almacenamiento, la geolocalización, las imágenes— y dejar en crudo lo que ya es estándar, como fetch o Response, que corre igual en todas partes y no necesita disfraz.

flowchart TD
Q1{necesitas modulos nativos o mucha CPU}
Q1 -->|si| NODE[servidor node o serverless node]
Q1 -->|no| Q2{prioridad latencia global minima}
Q2 -->|si| EDGE[cloudflare o edge]
Q2 -->|no| Q3{quieres escalar a cero y pagar por uso}
Q3 -->|si| SL[serverless en vercel o netlify]
Q3 -->|no| OWN[tu propio servidor node fijo]
style EDGE fill:#a6e3a1,color:#11111b
style NODE fill:#89b4fa,color:#11111b
El adapter convierte el destino en una decisión tardía, y eso es poder de negociación

La consecuencia más profunda de la arquitectura de adapters no es técnica sino estratégica, y tiene que ver con cuándo te comprometes con una plataforma. En el modelo tradicional, elegir dónde desplegar era una decisión temprana e irreversible: adoptabas el framework serverless de un proveedor, escribías contra su formato de función, y quedabas casado con él —cambiar significaba reescribir—. Astro invierte esa temporalidad. Como su motor habla el estándar de la web y toda la especificidad del destino vive comprimida en el adapter, la elección de plataforma se vuelve tardía: puedes construir el sitio entero sin haberla tomado, probarla en Node local, y decidir el destino real casi al final, cambiándolo después cuantas veces convenga. Esa demora tiene un valor que trasciende la comodidad: es poder de negociación y es seguro contra el arrepentimiento. El proveedor que sabe que no puedes irte te trata distinto del que sabe que portar te cuesta una tarde; una subida de precios, un límite nuevo, una caída repetida dejan de ser trampas y pasan a ser motivos para mudarte. Y como nadie predice bien la carga futura —el tráfico que tendrás, los picos, el presupuesto—, conservar la opción de cambiar es conservar la capacidad de corregir una decisión tomada con información incompleta, que es toda decisión de arquitectura. Pero esa opción no viene regalada con el adapter: se gana con disciplina. Cada vez que llamas a fs, a un binario nativo o a Astro.locals.runtime.env sin confinarlo, gastas un poco de esa libertad; cada vez que lo escondes tras una frontera propia, la conservas. El ingeniero que entiende esto no pregunta primero “dónde despliego”, sino “cómo escribo para no tener que responder eso todavía”. Porque la mejor plataforma no es la más barata ni la más rápida de hoy: es aquella de la que puedes marcharte mañana. Y programar contra el estándar, con el proveedor en cuarentena, es lo que mantiene esa puerta abierta.

⚔️ Elige con criterio y conserva la salida
  1. Toma un proyecto real e inventaría sus dependencias: marca cuáles asumen Node y cuáles correrían en el edge.
  2. Con ese inventario, recorre el árbol de decisión del diagrama y justifica qué plataforma le conviene.
  3. Envuelve un acceso a Astro.locals.runtime tras un módulo propio con una interfaz neutral, como en el ejemplo de Store.
  4. Cambia el adapter de node a cloudflare, anota todo lo que se rompe, y comprueba cuánto menos duele el port gracias a esa frontera.