Português

Explore o middleware do Next.js, um recurso poderoso para interceptar e modificar requisições. Aprenda a implementar autenticação, autorização, redirecionamento e testes A/B com exemplos práticos.

Middleware do Next.js: Dominando a Interceptação de Requisições para Aplicações Dinâmicas

O middleware do Next.js oferece uma maneira flexível e poderosa de interceptar e modificar requisições recebidas antes que elas cheguem às suas rotas. Essa capacidade permite que você implemente uma ampla gama de funcionalidades, desde autenticação e autorização até redirecionamento e testes A/B, tudo isso otimizando o desempenho. Este guia completo irá guiá-lo pelos conceitos centrais do middleware do Next.js e demonstrar como aproveitá-lo de forma eficaz.

O que é o Middleware do Next.js?

O middleware no Next.js é uma função que é executada antes que uma requisição seja concluída. Ele permite que você:

As funções de middleware são definidas no arquivo middleware.ts (ou middleware.js) na raiz do seu projeto. Elas são executadas para cada rota dentro da sua aplicação, ou para rotas específicas com base em matchers configuráveis.

Conceitos Chave e Benefícios

Objeto Request

O objeto request fornece acesso a informações sobre a requisição recebida, incluindo:

Objeto Response

As funções de middleware retornam um objeto Response para controlar o resultado da requisição. Você pode usar as seguintes respostas:

Matchers

Os matchers permitem que você especifique a quais rotas seu middleware deve ser aplicado. Você pode definir matchers usando expressões regulares ou padrões de caminho. Isso garante que seu middleware seja executado apenas quando necessário, melhorando o desempenho e reduzindo a sobrecarga.

Edge Runtime

O middleware do Next.js é executado no Edge Runtime, que é um ambiente de execução JavaScript leve que pode ser implantado perto de seus usuários. Essa proximidade minimiza a latência e melhora o desempenho geral da sua aplicação, especialmente para usuários distribuídos globalmente. O Edge Runtime está disponível na Edge Network da Vercel e em outras plataformas compatíveis. O Edge Runtime tem algumas limitações, especificamente o uso de APIs do Node.js.

Exemplos Práticos: Implementando Funcionalidades de Middleware

1. Autenticação

O middleware de autenticação pode ser usado para proteger rotas que exigem que os usuários estejam logados. Aqui está um exemplo de como implementar a autenticação 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 verifica a presença de um cookie auth_token. Se o cookie não for encontrado, o usuário é redirecionado para a página /login. O config.matcher especifica que este middleware deve ser executado apenas para rotas sob /dashboard.

Perspectiva Global: Adapte a lógica de autenticação para suportar vários métodos de autenticação (ex: OAuth, JWT) e integre-se com diferentes provedores de identidade (ex: Google, Facebook, Azure AD) para atender a usuários de diversas regiões.

2. Autorização

O middleware de autorização pode ser usado para controlar o acesso a recursos com base nas funções ou permissões do usuário. Por exemplo, você pode ter um painel de administração que apenas usuários específicos podem acessar.


// 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))
 }

 // Example: Fetch user roles from an API (replace with your actual logic)
 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 a função do usuário e verifica se ele tem a função de admin. Caso contrário, ele é redirecionado para uma página /unauthorized. Este exemplo usa um endpoint de API de exemplo. Substitua `https://api.example.com/userinfo` pelo endpoint real do seu servidor de autenticação.

Perspectiva Global: Esteja ciente das regulamentações de privacidade de dados (ex: GDPR, LGPD) ao lidar com dados de usuários. Implemente medidas de segurança apropriadas para proteger informações sensíveis e garantir a conformidade com as leis locais.

3. Redirecionamento

O middleware de redirecionamento pode ser usado para redirecionar usuários com base em sua localização, idioma ou outros critérios. Por exemplo, você pode redirecionar usuários para uma versão localizada do seu site com base no endereço IP deles.


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

export function middleware(request: NextRequest) {
 const country = request.geo?.country || 'US'; // Default to US if geo-location fails

 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 verifica o país do usuário com base em seu endereço IP e o redireciona para a versão localizada apropriada do site (/de para Alemanha, /fr para França). Se a geolocalização falhar, ele assume a versão dos EUA como padrão. Note que isso depende da propriedade geo estar disponível (por exemplo, quando implantado na Vercel).

Perspectiva Global: Garanta que seu site suporte múltiplos idiomas e moedas. Forneça aos usuários a opção de selecionar manualmente seu idioma ou região preferida. Use formatos de data e hora apropriados para cada localidade.

4. Teste A/B

O middleware pode ser usado para implementar testes A/B, atribuindo aleatoriamente os usuários a diferentes variantes de uma página e rastreando seu comportamento. Aqui está um exemplo 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 atribui os usuários à variante 'A' ou 'B'. Se um usuário ainda não tiver um cookie de variant, um é atribuído aleatoriamente e definido. Os usuários atribuídos à variante 'B' são reescritos para a página /variant-b. Você então rastrearia o desempenho de cada variante para determinar qual é mais eficaz.

Perspectiva Global: Considere as diferenças culturais ao projetar testes A/B. O que funciona bem em uma região pode não ter o mesmo efeito com usuários de outra. Garanta que sua plataforma de testes A/B esteja em conformidade com as regulamentações de privacidade em diferentes regiões.

5. Feature Flags

Feature flags (ou sinalizadores de funcionalidade) permitem que você ative ou desative recursos em sua aplicação sem implantar novo código. O middleware pode ser usado para determinar se um usuário deve ter acesso a um recurso específico com base em seu ID de usuário, localização ou outros critérios.


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

export async function middleware(request: NextRequest) {
 // Example: Fetch feature flags from an 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) {
 // Enable the new feature
 return NextResponse.next();
 } else {
 // Disable the new feature (e.g., redirect to an alternative page)
 return NextResponse.redirect(new URL('/alternative-page', request.url));
 }
}

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

Este middleware busca feature flags de uma API e verifica se a flag new_feature_enabled está definida. Se estiver, o usuário pode acessar a página /new-feature. Caso contrário, ele é redirecionado para uma /alternative-page.

Perspectiva Global: Use feature flags para lançar gradualmente novos recursos para usuários em diferentes regiões. Isso permite que você monitore o desempenho e resolva quaisquer problemas antes de liberar o recurso para um público mais amplo. Além disso, garanta que seu sistema de feature flagging escale globalmente e forneça resultados consistentes, independentemente da localização do usuário. Considere as restrições regulatórias regionais para o lançamento de recursos.

Técnicas Avançadas

Encadeamento de Middleware

Você pode encadear múltiplas funções de middleware para realizar uma série de operações em uma requisição. Isso pode ser útil para dividir lógicas complexas em módulos menores e mais gerenciáveis.


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

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

 // First middleware function
 const token = request.cookies.get('auth_token');
 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Second middleware function
 response.headers.set('x-middleware-custom', 'value');

 return response;
}

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

Este exemplo mostra dois middlewares em um. O primeiro realiza a autenticação e o segundo define um cabeçalho personalizado.

Usando Variáveis de Ambiente

Armazene informações sensíveis, como chaves de API e credenciais de banco de dados, em variáveis de ambiente em vez de codificá-las diretamente em suas funções de middleware. Isso melhora a segurança e facilita o gerenciamento da configuração da sua aplicação.


// 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'],
}

Neste exemplo, a API_KEY é recuperada de uma variável de ambiente.

Tratamento de Erros

Implemente um tratamento de erros robusto em suas funções de middleware para evitar que erros inesperados quebrem sua aplicação. Use blocos try...catch para capturar exceções e registrar erros adequadamente.


// 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(); // Or redirect to an error page
 }
}

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

Melhores Práticas

Solucionando Problemas Comuns

Conclusão

O middleware do Next.js é uma ferramenta poderosa para construir aplicações web dinâmicas e personalizadas. Ao dominar a interceptação de requisições, você pode implementar uma ampla gama de funcionalidades, desde autenticação e autorização até redirecionamento e testes A/B. Seguindo as melhores práticas descritas neste guia, você pode aproveitar o middleware do Next.js para criar aplicações de alto desempenho, seguras e escaláveis que atendam às necessidades de sua base de usuários global. Abrace o poder do middleware para desbloquear novas possibilidades em seus projetos Next.js e oferecer experiências de usuário excepcionais.