Ir al contenido
angel.library

Middleware

Repetir la misma comprobación (¿hay sesión?, ¿qué idioma pide el usuario?) al principio de cada página no escala. El middleware es una única función que Astro corre antes de cualquier página o endpoint, sin importar cuál — el lugar central para auth, logging, o para calcular una vez algo que varias páginas van a necesitar y dejarlo listo en locals.

Vive en src/middleware.ts (o src/middleware/index.ts). Exporta onRequest, siempre con ese nombre, nunca default.

src/middleware.ts
import { defineMiddleware } from "astro:middleware"
export const onRequest = defineMiddleware((context, next) => {
context.locals.tema = "oscuro"
return next()
})

defineMiddleware no es obligatorio, pero da tipado automático al context — sin él hay que tipar a mano.

context.locals — Compartir datos con la página

Sección titulada «context.locals — Compartir datos con la página»

Cualquier cosa guardada en locals está disponible en Astro.locals dentro de las páginas y en el context.locals de las actions/endpoints que corran después.

src/middleware.ts
export const onRequest = defineMiddleware((context, next) => {
context.locals.usuario = obtenerUsuarioDeCookie(context.cookies)
return next()
})
pages/perfil.astro
---
const usuario = Astro.locals.usuario
if (!usuario) return Astro.redirect("/login")
---

Para autocompletado tipado de Astro.locals, se declara la forma en env.d.ts:

env.d.ts
declare namespace App {
interface Locals {
usuario: { id: string; nombre: string } | null
}
}

Cada uno corre en orden; el control pasa al siguiente al llamar next().

src/middleware.ts
import { sequence } from "astro:middleware"
async function logging(context, next) {
console.log(context.url.pathname)
return next()
}
async function auth(context, next) {
context.locals.usuario = obtenerUsuarioDeCookie(context.cookies)
return next()
}
export const onRequest = sequence(logging, auth)

next(new Request(...)) cambia la request sin volver a correr el middleware; context.rewrite(...) la reescribe y sí vuelve a pasar por toda la cadena.

export const onRequest = defineMiddleware((context, next) => {
if (!context.locals.usuario && context.url.pathname.startsWith("/admin")) {
return context.rewrite("/login")
}
return next()
})
APIUso
onRequest(context, next) en src/middleware.tsPunto de entrada, corre antes de cada página/endpoint
defineMiddleware(fn)Igual, con tipado automático del context
context.localsCompartir datos entre middleware → página/endpoint/action
sequence(a, b, ...)Encadenar varios middlewares en orden
next()Pasar el control al siguiente middleware / renderizar la página
context.rewrite(destino)Servir otra ruta sin cambiar la URL, re-corriendo el middleware
  • locals no se puede reasignar completo (context.locals = {...} tira error) — solo se le agregan propiedades.
  • El middleware corre en build para páginas prerenderizadas y en cada request para las on-demand — si depende de algo que solo existe en runtime (headers reales, cookies del usuario), esa página necesita prerender = false.
  • Las Server Actions no pasan automáticamente por el middleware de páginas: si necesitas que también las cubra, hay que usar getActionContext() dentro del middleware.