Astro middleware

Qué es y cómo funciona un middleware en Astro.

control-deslizante-comparacion-imagenes

Un middleware es una función que se ejecuta antes de que una ruta sea servida. Nos permite modificar la Request, la Response o simplemente decidir si seguimos con el flujo normal o no.

El middleware se define en el archivo src/middleware.ts (o .js) y puede trabajar de forma sincrónica o asincrónica. Astro lo ejecuta automáticamente en cada solicitud que llega al servidor.

Un ejemplo básico:

src/middleware.ts
import { defineMiddleware } from "astro/middleware";
export const onRequest = defineMiddleware(async (context, next) => {
console.log("Interceptamos la ruta:", context.url.pathname);
return next(); // Dejamos que continúe normalmente
});

Con este patrón base, ya podemos empezar a tomar decisiones personalizadas sobre cada petición que entra.

¿Cuándo tiene sentido usar middleware?

El middleware en Astro no reemplaza los endpoints API o la lógica de servidor en las páginas .astro o .ts. Más bien, se usa para:

  • Proteger rutas que requieren autenticación.
  • Hacer redirecciones según el idioma del navegador.
  • Bloquear bots o tráfico sospechoso.
  • Agregar headers personalizados a ciertas rutas.
  • Registrar métricas de acceso.

Imaginemos que queremos proteger todas las rutas bajo /admin para que sólo se pueda acceder si el usuario tiene una cookie auth=true. Podríamos hacer algo como esto:

export const onRequest = defineMiddleware(async ({ url, cookies }, next) => {
if (url.pathname.startsWith("/admin")) {
const auth = cookies.get("auth");
if (auth?.value !== "true") {
return new Response(null, {
status: 302,
headers: {
Location: "/login",
},
});
}
}
return next();
});

Simple, directo y sin necesidad de repetir esta lógica en cada archivo .astro.

Limitaciones

Aunque el middleware es útil, tiene algunas limitaciones. No puede usarse para renderizar HTML, acceder a componentes de UI o modificar directamente el contenido de una página. Sirve más bien como un filtro previo.

Tampoco podemos tener múltiples archivos de middleware (por ahora). Si necesitamos separar la lógica, conviene dividir en funciones auxiliares dentro del mismo archivo:

function protegerRutaAdmin(context) {
const { url, cookies } = context;
if (url.pathname.startsWith("/admin")) {
const auth = cookies.get("auth");
if (auth?.value !== "true") {
return new Response(null, {
status: 302,
headers: { Location: "/login" },
});
}
}
}
export const onRequest = defineMiddleware(async (context, next) => {
const resultado = protegerRutaAdmin(context);
if (resultado) return resultado;
return next();
});

Esta organización nos ayuda a mantener el código limpio y legible a medida que crece.

Middleware y rutas estáticas

Algo importante: si generamos el sitio de forma completamente estática (modo SSG), el middleware no se ejecuta. Esto tiene sentido, ya que el middleware ocurre en el lado del servidor.

Así que si queremos usar un middleware, necesitamos tener al menos SSR habilitado para las páginas que lo requieran. Astro permite combinar ambos modos, así que podemos elegir según la necesidad.

Conclusión

Usar middleware en Astro nos da un punto de control previo a que se sirva cada ruta. No es un sistema complejo, pero puede ser clave cuando necesitamos lógica común que se aplique a muchas rutas. Desde validaciones hasta redirecciones, pasando por detalles más específicos como logging o headers personalizados, es una herramienta que suma sin complicar.

Y lo bueno es que, al estar basado en funciones simples, podemos adaptarlo a nuestro estilo sin grandes obstáculos.