Configurar el almacenamiento: drivers de sesión
Dónde viven de verdad los datos de sesión. El almacenamiento sobre unstorage y su catálogo de drivers: memoria para desarrollo, filesystem para una sola máquina, Workers KV en el edge y Redis para varias instancias. Cómo se registra un driver con los ayudantes de sessionDrivers o con el par entrypoint y config, cuándo cada adapter trae uno por defecto, y qué compromiso de persistencia, latencia y reparto elige cada opción.
El driver es el hogar físico de los datos
Astro no reinventa el almacenamiento: se apoya en unstorage, una biblioteca que expone una interfaz común —guardar, leer y borrar por clave— sobre decenas de backends distintos. Cada backend es un driver, y todos hablan el mismo dialecto de tres verbos, de modo que Astro puede volcar una sesión en cualquiera de ellos sin conocer sus tripas. Esa uniformidad es la que te deja empezar con un archivo en disco y terminar en un Redis distribuido sin tocar una sola línea de tu código de páginas.
Registrar un driver tiene dos formas. La primera son los ayudantes que Astro exporta desde astro/config bajo sessionDrivers, pensados para los backends más comunes. La segunda, más general, describe el driver como un par entrypoint —el módulo que lo implementa— y config —sus opciones serializables—, lo que permite usar cualquier driver del catálogo de unstorage, aunque Astro no tenga un ayudante dedicado.
// astro.config.mjs — dos maneras de nombrar un driverimport { defineConfig, sessionDrivers } from 'astro/config';export default defineConfig({ session: { // 1) ayudante dedicado driver: sessionDrivers.redis({ url: process.env.REDIS_URL }), // 2) cualquier driver de unstorage por entrypoint (alternativa) // driver: { entrypoint: 'unstorage/drivers/fs', config: { base: './.sessions' } }, },});El catálogo: memoria, filesystem, KV y Redis
Cada driver ocupa un punto distinto en el espacio de compromisos. No hay un “mejor”: hay el adecuado para tu topología de despliegue.
El driver en memoria guarda las sesiones en una estructura del propio proceso. Es instantáneo y no pide infraestructura, pero paga dos precios: los datos mueren al reiniciar el servidor, y no se comparten entre procesos. Sirve para desarrollo y pruebas, nunca para producción con más de una instancia. El ayudante sessionDrivers.lruCache ofrece justamente eso —un caché en memoria con un tope de entradas— y deja claro su carácter efímero.
session: { driver: sessionDrivers.lruCache({ max: 1000 }) }El driver de filesystem escribe cada sesión como un archivo en disco. Sobrevive a reinicios y no necesita servicios externos, pero ata las sesiones a una máquina: si despliegas dos instancias detrás de un balanceador, cada una verá sus propios archivos y un usuario perdería la sesión al saltar de una a otra. Es la opción por defecto que instala el adapter de Node, ideal para un servidor único.
// filesystem via unstorage, util en una sola maquinasession: { driver: { entrypoint: 'unstorage/drivers/fs', config: { base: './.sessions' } } }Workers KV es el almacén clave-valor de Cloudflare, distribuido por el edge. Su fuerte es la lectura global de baja latencia desde cualquier región; su matiz es la consistencia eventual —una escritura tarda un instante en propagarse a todos los nodos—. Es el driver por defecto del adapter de Cloudflare, que lo enlaza a un binding declarado en tu proyecto.
// KV de Cloudflare por bindingsession: { driver: { entrypoint: 'unstorage/drivers/cloudflare-kv-binding', config: { binding: 'SESSION' } } }Redis es el caballo de batalla de las sesiones en producción con varias instancias. Es un almacén en memoria, rapidísimo, accesible por todas tus máquinas a la vez y con expiración por clave nativa —perfecta para una sesión con caducidad—. A cambio, es un servicio que hay que operar o contratar. Cuando tu aplicación escala horizontalmente, Redis (o un KV equivalente) deja de ser un lujo y pasa a ser el requisito para que la sesión sea coherente en toda la flota.
La regla de decisión es topológica, no de gusto. Una sola máquina admite filesystem; varias instancias detrás de un balanceador exigen un almacén compartido —Redis o KV—; el edge distribuido pide KV por su lectura global; y el desarrollo se conforma con memoria. Hay un caso que atrapa a muchos: en una plataforma serverless y sin estado, ni siquiera el filesystem sobrevive entre invocaciones, porque cada una puede correr en una máquina distinta y efímera. Ahí un almacén externo no es una preferencia, es una condición de existencia de la sesión.
Configuración en build y configuración en runtime
Hay un detalle que muerde en producción si se ignora. Los drivers se resuelven en tiempo de build, y cualquier variable de entorno usada dentro de la configuración del driver queda incrustada en el artefacto compilado. Si tu REDIS_URL cambia entre entornos, no basta con leerla en astro.config.mjs: quedaría congelada la del build. La salida limpia es mover la construcción del driver a su propio módulo —un entrypoint— que se ejecuta en runtime y lee las credenciales entonces.
// src/session-driver.ts — se evalua en runtime, no en el buildimport type { SessionDriver } from 'astro';import redisDriver from 'unstorage/drivers/redis';import { REDIS_HOST, REDIS_PORT } from 'astro:env';export default function (): SessionDriver { return redisDriver({ host: REDIS_HOST, port: REDIS_PORT });}// astro.config.mjssession: { driver: { entrypoint: new URL('./src/session-driver.ts', import.meta.url) } }Con ese entrypoint, el módulo se importa y ejecuta en el servidor —no en el build—, de modo que astro:env le entrega los valores reales del entorno donde corre y la credencial deja de quedar congelada en el artefacto. Es la diferencia entre configurar qué driver usar en tiempo de compilación y resolver con qué datos conectarlo en tiempo de ejecución.
Un driver a medida en tres funciones
Bajo toda esta variedad hay una interfaz diminuta, y verla desnuda desmitifica el almacenamiento de una vez. Un SessionDriver de Astro es un objeto con tres métodos asíncronos —setItem, getItem y removeItem— y nada más. Si sabes guardar, leer y borrar un valor por clave en tu almacén favorito, ya sabes escribir un driver de sesión.
// src/mi-driver.tsimport type { SessionDriver } from 'astro';import { LRUCache } from 'lru-cache';export default function (config: { max?: number }): SessionDriver { const cache = new LRUCache<string, any>({ max: config.max ?? 500 }); return { setItem: async (clave, valor) => { cache.set(clave, valor); }, getItem: async (clave) => cache.get(clave), removeItem: async (clave) => { cache.delete(clave); }, };}Rara vez tendrás que llegar tan lejos: los tipos de unstorage son compatibles con los de Astro, así que casi siempre reexportas un driver del catálogo y le pasas su configuración, sin escribir la lógica de almacenamiento. Pero conocer el contrato mínimo tiene valor: explica por qué hay tantos backends, por qué migrar entre ellos no roza tu código de páginas, y te deja implementar un almacén exótico —una tabla de tu propia base de datos, un servicio interno— el día que ninguno del catálogo encaje.