Español

Descubre el middleware de Next.js para interceptar solicitudes. Aprende a implementar autenticación, redirección y pruebas A/B con ejemplos prácticos.

Middleware en Next.js: Dominando la Intercepción de Solicitudes para Aplicaciones Dinámicas

El middleware de Next.js proporciona una forma flexible y potente de interceptar y modificar las solicitudes entrantes antes de que lleguen a tus rutas. Esta capacidad te permite implementar una amplia gama de funcionalidades, desde autenticación y autorización hasta redirección y pruebas A/B, todo mientras optimizas el rendimiento. Esta guía completa te explicará los conceptos centrales del middleware de Next.js y te demostrará cómo aprovecharlo eficazmente.

¿Qué es el Middleware de Next.js?

El middleware en Next.js es una función que se ejecuta antes de que se complete una solicitud. Te permite:

Las funciones de middleware se definen en el archivo middleware.ts (o middleware.js) en la raíz de tu proyecto. Se ejecutan para cada ruta dentro de tu aplicación, o para rutas específicas basadas en matchers configurables.

Conceptos Clave y Beneficios

Objeto Request

El objeto request proporciona acceso a la información sobre la solicitud entrante, incluyendo:

Objeto Response

Las funciones de middleware devuelven un objeto Response para controlar el resultado de la solicitud. Puedes usar las siguientes respuestas:

Matchers

Los matchers te permiten especificar a qué rutas se debe aplicar tu middleware. Puedes definir matchers usando expresiones regulares o patrones de ruta. Esto asegura que tu middleware solo se ejecute cuando sea necesario, mejorando el rendimiento y reduciendo la sobrecarga.

Edge Runtime

El middleware de Next.js se ejecuta en el Edge Runtime, que es un entorno de ejecución de JavaScript ligero que se puede desplegar cerca de tus usuarios. Esta proximidad minimiza la latencia y mejora el rendimiento general de tu aplicación, especialmente para usuarios distribuidos globalmente. El Edge Runtime está disponible en la Edge Network de Vercel y otras plataformas compatibles. El Edge Runtime tiene algunas limitaciones, específicamente el uso de APIs de Node.js.

Ejemplos Prácticos: Implementando Funcionalidades con Middleware

1. Autenticación

El middleware de autenticación se puede usar para proteger rutas que requieren que los usuarios hayan iniciado sesión. Aquí tienes un ejemplo de cómo implementar la autenticación usando cookies:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

Este middleware comprueba la presencia de una cookie auth_token. Si no se encuentra la cookie, el usuario es redirigido a la página /login. El config.matcher especifica que este middleware solo debe ejecutarse para las rutas bajo /dashboard.

Perspectiva Global: Adapta la lógica de autenticación para admitir varios métodos (p. ej., OAuth, JWT) e intégrala con diferentes proveedores de identidad (p. ej., Google, Facebook, Azure AD) para atender a usuarios de diversas regiones.

2. Autorización

El middleware de autorización se puede usar para controlar el acceso a recursos según los roles o permisos de los usuarios. Por ejemplo, podrías tener un panel de administración al que solo puedan acceder usuarios específicos.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 const token = request.cookies.get('auth_token');

 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Ejemplo: Obtener roles de usuario desde una API (reemplaza con tu lógica real)
 const userResponse = await fetch('https://api.example.com/userinfo', {
 headers: {
 Authorization: `Bearer ${token}`,
 },
 });
 const userData = await userResponse.json();

 if (userData.role !== 'admin') {
 return NextResponse.redirect(new URL('/unauthorized', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/admin/:path*'],
}

Este middleware recupera el rol del usuario y comprueba si tiene el rol de admin. Si no es así, se le redirige a una página /unauthorized. Este ejemplo utiliza un endpoint de API de marcador de posición. Reemplaza `https://api.example.com/userinfo` con el endpoint de tu servidor de autenticación real.

Perspectiva Global: Ten en cuenta las regulaciones de privacidad de datos (p. ej., GDPR, CCPA) al manejar datos de usuario. Implementa medidas de seguridad adecuadas para proteger la información sensible y garantizar el cumplimiento de las leyes locales.

3. Redirección

El middleware de redirección se puede usar para redirigir a los usuarios según su ubicación, idioma u otros criterios. Por ejemplo, podrías redirigir a los usuarios a una versión localizada de tu sitio web según su dirección IP.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const country = request.geo?.country || 'US'; // Por defecto US si la geolocalización falla

 if (country === 'DE') {
 return NextResponse.redirect(new URL('/de', request.url))
 }

 if (country === 'FR') {
 return NextResponse.redirect(new URL('/fr', request.url))
 }

 return NextResponse.next()
}

export const config = {
 matcher: ['/'],
}

Este middleware comprueba el país del usuario basándose en su dirección IP y lo redirige a la versión localizada apropiada del sitio web (/de para Alemania, /fr para Francia). Si la geolocalización falla, se usa por defecto la versión de EE. UU. Ten en cuenta que esto depende de que la propiedad geo esté disponible (p. ej., al desplegar en Vercel).

Perspectiva Global: Asegúrate de que tu sitio web admita múltiples idiomas y monedas. Proporciona a los usuarios la opción de seleccionar manualmente su idioma o región preferidos. Utiliza los formatos de fecha y hora apropiados para cada configuración regional.

4. Pruebas A/B

El middleware se puede utilizar para implementar pruebas A/B asignando aleatoriamente a los usuarios a diferentes variantes de una página y rastreando su comportamiento. Aquí hay un ejemplo simplificado:


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

function getRandomVariant() {
 return Math.random() < 0.5 ? 'A' : 'B';
}

export function middleware(request: NextRequest) {
 let variant = request.cookies.get('variant')?.value;

 if (!variant) {
 variant = getRandomVariant();
 const response = NextResponse.next();
 response.cookies.set('variant', variant);
 return response;
 }

 if (variant === 'B') {
 return NextResponse.rewrite(new URL('/variant-b', request.url));
 }

 return NextResponse.next();
}

export const config = {
 matcher: ['/'],
}

Este middleware asigna a los usuarios a la variante 'A' o 'B'. Si un usuario aún no tiene una cookie de variant, se le asigna una al azar y se establece. Los usuarios asignados a la variante 'B' son reescritos a la página /variant-b. Luego, rastrearías el rendimiento de cada variante para determinar cuál es más efectiva.

Perspectiva Global: Considera las diferencias culturales al diseñar pruebas A/B. Lo que funciona bien en una región puede no resonar con los usuarios de otra. Asegúrate de que tu plataforma de pruebas A/B cumpla con las regulaciones de privacidad en diferentes regiones.

5. Feature Flags

Las feature flags (o indicadores de funcionalidad) te permiten habilitar o deshabilitar funcionalidades en tu aplicación sin desplegar nuevo código. El middleware se puede usar para determinar si un usuario debe tener acceso a una funcionalidad específica según su ID de usuario, ubicación u otros criterios.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 // Ejemplo: Obtener feature flags desde una API
 const featureFlagsResponse = await fetch('https://api.example.com/featureflags', {
 headers: {
 'X-User-Id': 'user123',
 },
 });
 const featureFlags = await featureFlagsResponse.json();

 if (featureFlags.new_feature_enabled) {
 // Habilitar la nueva funcionalidad
 return NextResponse.next();
 } else {
 // Deshabilitar la nueva funcionalidad (p. ej., redirigir a una página alternativa)
 return NextResponse.redirect(new URL('/alternative-page', request.url));
 }
}

export const config = {
 matcher: ['/new-feature'],
}

Este middleware obtiene feature flags de una API y comprueba si el indicador new_feature_enabled está activado. Si lo está, el usuario puede acceder a la página /new-feature. De lo contrario, es redirigido a una /alternative-page.

Perspectiva Global: Usa feature flags para lanzar gradualmente nuevas funcionalidades a usuarios en diferentes regiones. Esto te permite monitorear el rendimiento y abordar cualquier problema antes de lanzar la funcionalidad a una audiencia más amplia. Además, asegúrate de que tu sistema de feature flags escale globalmente y proporcione resultados consistentes independientemente de la ubicación del usuario. Considera las restricciones regulatorias regionales para los lanzamientos de funcionalidades.

Técnicas Avanzadas

Encadenamiento de Middleware

Puedes encadenar múltiples funciones de middleware para realizar una serie de operaciones en una solicitud. Esto puede ser útil para desglosar lógicas complejas en módulos más pequeños y manejables.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
 const response = NextResponse.next();

 // Primera función de middleware
 const token = request.cookies.get('auth_token');
 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Segunda función de middleware
 response.headers.set('x-middleware-custom', 'value');

 return response;
}

export const config = {
 matcher: ['/dashboard/:path*'],
}

Este ejemplo muestra dos middlewares en uno. El primero realiza la autenticación y el segundo establece un encabezado personalizado.

Uso de Variables de Entorno

Almacena información sensible, como claves de API y credenciales de base de datos, en variables de entorno en lugar de codificarlas directamente en tus funciones de middleware. Esto mejora la seguridad y facilita la gestión de la configuración de tu aplicación.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const API_KEY = process.env.API_KEY;

export async function middleware(request: NextRequest) {
 const response = await fetch('https://api.example.com/data', {
 headers: {
 'X-API-Key': API_KEY,
 },
 });

 // ...
}

export const config = {
 matcher: ['/data'],
}

En este ejemplo, la API_KEY se recupera de una variable de entorno.

Manejo de Errores

Implementa un manejo de errores robusto en tus funciones de middleware para evitar que errores inesperados bloqueen tu aplicación. Usa bloques try...catch para capturar excepciones y registrar errores adecuadamente.


// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export async function middleware(request: NextRequest) {
 try {
 const response = await fetch('https://api.example.com/data');
 // ...
 } catch (error) {
 console.error('Error fetching data:', error);
 return NextResponse.error(); // O redirigir a una página de error
 }
}

export const config = {
 matcher: ['/data'],
}

Mejores Prácticas

Solución de Problemas Comunes

Conclusión

El middleware de Next.js es una herramienta poderosa para construir aplicaciones web dinámicas y personalizadas. Al dominar la intercepción de solicitudes, puedes implementar una amplia gama de funcionalidades, desde autenticación y autorización hasta redirección y pruebas A/B. Siguiendo las mejores prácticas descritas en esta guía, puedes aprovechar el middleware de Next.js para crear aplicaciones de alto rendimiento, seguras y escalables que satisfagan las necesidades de tu base de usuarios global. Aprovecha el poder del middleware para desbloquear nuevas posibilidades en tus proyectos de Next.js y ofrecer experiencias de usuario excepcionales.

Middleware en Next.js: Dominando la Intercepción de Solicitudes para Aplicaciones Dinámicas | MLOG