¿Qué es el middleware? Guía para principiantes
Entiende el middleware desde primeros principios, por qué los SaaS lo usan y cómo funciona nuestro middleware en Next.js con i18n y request IDs — además de cómo personalizarlo.
Middleware en una frase
El middleware es código que se ejecuta entre una solicitud entrante y tu handler final, para inspeccionar, redirigir o añadir headers antes de que corra la página o API.
Middleware 101 (perspectiva de principiante)
Imagina una solicitud como un auto en la carretera. El middleware es el punto de control: todas las solicitudes pasan por ahí. En ese punto puedes leer la solicitud, tomar decisiones y opcionalmente cambiar la respuesta (o incluso detenerla antes).
Usos comunes en apps web:
- Puertas de autenticación: comprobar si el usuario está logueado, si no, redirigir.
- Internacionalización (i18n): enviar al usuario al locale correcto según URL o preferencias.
- Logging y trazas: adjuntar un
request_idpara correlacionar logs de punta a punta. - Feature flags / A/B testing: asignar usuarios a experimentos temprano.
- Seguridad y rate limiting: bloquear tráfico abusivo o reforzar headers.
En Next.js, el middleware vive en un archivo especial middleware.ts y corre en el edge (muy temprano y muy rápido) para las rutas que coinciden. Puede reescribir/redirigir solicitudes, leer o setear headers y cortar la respuesta antes de que se ejecuten los handlers.
Por qué ayuda (y cuándo lo necesitas)
El middleware centraliza preocupaciones transversales (lógica que aplica a muchas rutas), así no repites lo mismo en cada página o API:
- Un solo lugar para reglas: menos chances de olvidar auth o locale.
- Observabilidad consistente: todas las requests tienen el mismo
request_idy headers. - Decisiones rápidas: redirige o niega temprano, ahorrando trabajo al servidor.
- Handlers más limpios: la lógica de negocio queda en páginas/APIs, no en el plumbing.
Escenarios SaaS donde brilla:
- Apps localizadas que deben mantener URLs
/:locale/...consistentes. - Apps que dependen de IDs de correlación por request para logs y trazas.
- Gatekeeping de funciones de pago o zonas admin (con manejo cuidadoso de cookies).
- Experimentación (A/B), geo routing y normalización de headers.
Nuestro middleware: qué hace hoy
Incluimos un default mínimo y seguro en src/middleware.ts que combina el routing i18n de next-intl con propagación ligera de request IDs.
Comportamientos clave:
- Routing de locales: delega a
next-intlusando nuestra configuración (src/i18n/routing.ts,src/i18n/locale.ts). Mantiene URLs como/en/...,/fr/...consistentes y permite detección opcional de locale. - Header de request ID: asegura que toda respuesta lleve
x-request-idpara correlación de logs. Si la request ya trae uno, lo reutilizamos; si no, generamos un UUID. - Matcher conservador: corre en páginas localizadas y rutas generales; salta assets, internals de Next, rutas API y
admin.
Código actual (recortado para claridad):
// src/middleware.ts
import createMiddleware from 'next-intl/middleware';
import { NextRequest } from 'next/server';
import { routing } from '@/i18n/routing';
const intlMiddleware = createMiddleware(routing);
export default function middleware(request: NextRequest) {
const existing = request.headers.get('x-request-id');
const requestId = existing || crypto.randomUUID();
const res = intlMiddleware(request);
// Solo visibilidad; no mutar cookies/headers de la request.
res.headers.set('x-request-id', requestId);
return res;
}
export const config = {
matcher: [
'/',
'/(en|en-US|zh|zh-CN|zh-TW|zh-HK|zh-MO|ja|ko|ru|fr|de|ar|es|it)/:path*',
'/((?!api|_next|_vercel|admin|.*\\..*).*)',
],
};Configuración de locales aquí:
// src/i18n/routing.ts
import { defaultLocale, localeDetection, localePrefix, locales } from './locale';
import { defineRouting } from 'next-intl/routing';
export const routing = defineRouting({ locales, defaultLocale, localePrefix, localeDetection });Locales declarados aquí (edita para añadir/quitar):
// src/i18n/locale.ts
export const locales = ['en', 'zh', 'es', 'fr', 'ja'];
export const defaultLocale = 'en';
export const localePrefix = 'always';
export const localeDetection = process.env.NEXT_PUBLIC_LOCALE_DETECTION === 'true';Notas:
- Evitamos cambiar headers/cookies de la request en middleware para mantener Better Auth estable. Solo seteamos un header de respuesta.
- El matcher excluye
api,_next, assets yadminpor defecto. Ajusta si necesitas middleware allí.
Cómo personalizar (con seguridad)
Antes de personalizar, valida que la lógica realmente pertenece al middleware (transversal, decisión temprana) y no a una ruta específica. Cuando sí aplica, usa patrones como estos:
Añadir headers personalizados
export default function middleware(request: NextRequest) {
const res = intlMiddleware(request);
res.headers.set('x-feature-flag', 'on');
return res;
}Redirigir o reescribir rutas
import { NextResponse } from 'next/server';
export default function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
if (pathname === '/old') {
return NextResponse.redirect(new URL('/new', request.url));
}
return intlMiddleware(request);
}Incluir/excluir rutas con matcher
export const config = {
matcher: [
'/',
// Volver a incluir admin quitándolo del negative lookahead
// o haciendo match explícito:
'/admin/:path*',
'/(en|fr|ja|zh|es)/:path*',
'/((?!api|_next|_vercel|.*\\..*).*)',
],
};Usar request IDs dentro de handlers
// En un handler de Node
import { headers } from 'next/headers';
export async function GET() {
const h = headers();
const request_id = h.get('x-request-id');
// adjunta a logs, trazas de DB, etc.
return new Response('ok');
}Ajustes de internacionalización
- Cambia
NEXT_PUBLIC_LOCALE_DETECTIONatruepara activar auto detección. - Actualiza
src/i18n/locale.tspara añadir/quitar locales y nombres. - Mantén
content/docs,messages/ysrc/i18nsincronizados al agregar un locale.
Auth y flujos sensibles
- Si agregas gatekeeping de auth en middleware, evita mutar cookies; prefiere redirecciones y checks de solo lectura.
- Verifica que las rutas API protegidas también se validen en el handler — middleware es una comodidad, no tu único guardia.
Cuándo NO usar middleware
- Lógica específica de una ruta que no aplica de forma amplia.
- Trabajo async pesado (llamadas a DB) que debe ir en el handler.
- Transformar cuerpos grandes de requests (middleware no puede leer el body).
Mantén el middleware rápido y enfocado. Si crece demasiado, mueve la lógica especializada a servicios y llámala desde los handlers.
TL;DR
- Middleware corre temprano y de forma central — ideal para i18n, headers, redirects y guardas ligeras.
- Nuestro middleware por defecto maneja locale routing y setea
x-request-idpara trazabilidad. - Personaliza con
config.matcher, headers de respuesta y redirects seguros; evita tocar cookies de auth. - Manténlo pequeño, rápido y consistente entre locales.
¿Qué es Next.js? Guía para principiantes
Por qué Next.js encaja con SaaS: routing, SSR/SSG y API routes en un solo repo — guía completa y fácil de seguir.
Frontend vs Backend vs Full‑Stack para principiantes SaaS
Guía amigable para entender frontend, backend y full‑stack en SaaS, con una analogía fácil y ejemplos de un stack moderno.