Saltar al contenido

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.

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.

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.

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.

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.

JS
// de un servidor Node...import node from '@astrojs/node';export default defineConfig({ adapter: node({ mode: 'standalone' }) });// ...al edge de Cloudflare, en una lineaimport 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.

TypeScript
// src/lib/storage.ts  ->  la unica puerta al almacenamiento del proveedorexport interface Store {  get(clave: string): Promise<string | null>;  set(clave: string, valor: string): Promise<void>;}// implementacion respaldada por un binding KV de Cloudflareexport 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.

TypeScript
// la misma interfaz Store, ahora sobre Redis en un servidor Nodeimport { 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.