Italiano

Esplora il middleware di Next.js, una potente funzionalità per intercettare e modificare le richieste in entrata. Impara come implementare autenticazione, autorizzazione, reindirizzamento e A/B testing con esempi pratici.

Middleware di Next.js: Padroneggiare l'intercettazione delle richieste per applicazioni dinamiche

Il middleware di Next.js offre un modo flessibile e potente per intercettare e modificare le richieste in entrata prima che raggiungano le tue route. Questa capacità ti permette di implementare una vasta gamma di funzionalità, dall'autenticazione e autorizzazione al reindirizzamento e A/B testing, il tutto ottimizzando le prestazioni. Questa guida completa ti guiderà attraverso i concetti fondamentali del middleware di Next.js e ti dimostrerà come sfruttarlo efficacemente.

Cos'è il Middleware di Next.js?

Il middleware in Next.js è una funzione che viene eseguita prima che una richiesta sia completata. Ti permette di:

Le funzioni middleware sono definite nel file middleware.ts (o middleware.js) alla radice del tuo progetto. Vengono eseguite per ogni route all'interno della tua applicazione, o per route specifiche in base a matcher configurabili.

Concetti Chiave e Vantaggi

Oggetto Request

L'oggetto request fornisce accesso alle informazioni sulla richiesta in entrata, tra cui:

Oggetto Response

Le funzioni middleware restituiscono un oggetto Response per controllare l'esito della richiesta. Puoi usare le seguenti risposte:

Matcher

I matcher ti permettono di specificare a quali route il tuo middleware dovrebbe essere applicato. Puoi definire i matcher usando espressioni regolari o pattern di percorso. Questo assicura che il tuo middleware venga eseguito solo quando necessario, migliorando le prestazioni e riducendo il carico.

Edge Runtime

Il middleware di Next.js viene eseguito sull'Edge Runtime, un ambiente di esecuzione JavaScript leggero che può essere distribuito vicino ai tuoi utenti. Questa prossimità minimizza la latenza e migliora le prestazioni complessive della tua applicazione, specialmente per utenti distribuiti a livello globale. L'Edge Runtime è disponibile sull'Edge Network di Vercel e su altre piattaforme compatibili. L'Edge Runtime ha alcune limitazioni, in particolare l'uso delle API di Node.js.

Esempi Pratici: Implementare Funzionalità con il Middleware

1. Autenticazione

Il middleware di autenticazione può essere utilizzato per proteggere le route che richiedono agli utenti di essere autenticati. Ecco un esempio di come implementare l'autenticazione usando i cookie:


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

Questo middleware verifica la presenza di un cookie auth_token. Se il cookie non viene trovato, l'utente viene reindirizzato alla pagina /login. Il config.matcher specifica che questo middleware deve essere eseguito solo per le route sotto /dashboard.

Prospettiva Globale: Adatta la logica di autenticazione per supportare vari metodi di autenticazione (es., OAuth, JWT) e integrare con diversi provider di identità (es., Google, Facebook, Azure AD) per soddisfare utenti di diverse regioni.

2. Autorizzazione

Il middleware di autorizzazione può essere usato per controllare l'accesso alle risorse in base ai ruoli o ai permessi degli utenti. Ad esempio, potresti avere una dashboard di amministrazione a cui solo utenti specifici possono accedere.


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

 // Esempio: Recupera i ruoli dell'utente da un'API (sostituisci con la tua logica effettiva)
 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*'],
}

Questo middleware recupera il ruolo dell'utente e verifica se ha il ruolo di admin. In caso contrario, viene reindirizzato a una pagina /unauthorized. Questo esempio utilizza un endpoint API segnaposto. Sostituisci `https://api.example.com/userinfo` con l'endpoint effettivo del tuo server di autenticazione.

Prospettiva Globale: Sii consapevole delle normative sulla privacy dei dati (es., GDPR, CCPA) quando gestisci i dati degli utenti. Implementa misure di sicurezza appropriate per proteggere le informazioni sensibili e garantire la conformità con le leggi locali.

3. Reindirizzamento

Il middleware di reindirizzamento può essere usato per reindirizzare gli utenti in base alla loro posizione, lingua o altri criteri. Ad esempio, potresti reindirizzare gli utenti a una versione localizzata del tuo sito web in base al loro indirizzo 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'; // Predefinito a US se la geolocalizzazione fallisce

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

Questo middleware controlla il paese dell'utente in base al suo indirizzo IP e lo reindirizza alla versione localizzata appropriata del sito web (/de per la Germania, /fr per la Francia). Se la geolocalizzazione fallisce, passa alla versione statunitense. Nota che questo dipende dalla disponibilità della proprietà geo (ad esempio, quando distribuito su Vercel).

Prospettiva Globale: Assicurati che il tuo sito web supporti più lingue e valute. Fornisci agli utenti la possibilità di selezionare manualmente la loro lingua o regione preferita. Usa formati di data e ora appropriati per ogni locale.

4. A/B Testing

Il middleware può essere utilizzato per implementare l'A/B testing assegnando casualmente gli utenti a diverse varianti di una pagina e monitorando il loro comportamento. Ecco un esempio semplificato:


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

Questo middleware assegna gli utenti alla variante 'A' o 'B'. Se un utente non ha già un cookie variant, ne viene assegnato e impostato uno casualmente. Gli utenti assegnati alla variante 'B' vengono riscritti alla pagina /variant-b. Dovresti quindi monitorare le prestazioni di ogni variante per determinare quale sia più efficace.

Prospettiva Globale: Considera le differenze culturali durante la progettazione degli A/B test. Ciò che funziona bene in una regione potrebbe non avere lo stesso effetto sugli utenti di un'altra. Assicurati che la tua piattaforma di A/B testing sia conforme alle normative sulla privacy delle diverse regioni.

5. Feature Flag

I feature flag ti permettono di abilitare o disabilitare funzionalità nella tua applicazione senza distribuire nuovo codice. Il middleware può essere usato per determinare se un utente debba avere accesso a una specifica funzionalità in base al suo ID utente, posizione o altri criteri.


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

export async function middleware(request: NextRequest) {
 // Esempio: Recupera i feature flag da un'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) {
 // Abilita la nuova funzionalità
 return NextResponse.next();
 } else {
 // Disabilita la nuova funzionalità (es., reindirizza a una pagina alternativa)
 return NextResponse.redirect(new URL('/alternative-page', request.url));
 }
}

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

Questo middleware recupera i feature flag da un'API e verifica se il flag new_feature_enabled è impostato. Se lo è, l'utente può accedere alla pagina /new-feature. Altrimenti, viene reindirizzato a una pagina /alternative-page.

Prospettiva Globale: Usa i feature flag per rilasciare gradualmente nuove funzionalità agli utenti in diverse regioni. Ciò ti consente di monitorare le prestazioni e risolvere eventuali problemi prima di rilasciare la funzionalità a un pubblico più ampio. Inoltre, assicurati che il tuo sistema di feature flagging scali a livello globale e fornisca risultati coerenti indipendentemente dalla posizione dell'utente. Considera i vincoli normativi regionali per il rilascio delle funzionalità.

Tecniche Avanzate

Concatenamento di Middleware

Puoi concatenare più funzioni middleware per eseguire una serie di operazioni su una richiesta. Questo può essere utile per suddividere logiche complesse in moduli più piccoli e gestibili.


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

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

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

 // Seconda funzione middleware
 response.headers.set('x-middleware-custom', 'value');

 return response;
}

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

Questo esempio mostra due middleware in uno. Il primo esegue l'autenticazione e il secondo imposta un header personalizzato.

Utilizzo delle Variabili d'Ambiente

Memorizza le informazioni sensibili, come chiavi API e credenziali del database, in variabili d'ambiente anziché scriverle direttamente nel codice delle tue funzioni middleware. Ciò migliora la sicurezza e facilita la gestione della configurazione della tua applicazione.


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

In questo esempio, la API_KEY viene recuperata da una variabile d'ambiente.

Gestione degli Errori

Implementa una gestione degli errori robusta nelle tue funzioni middleware per evitare che errori imprevisti blocchino la tua applicazione. Usa blocchi try...catch per catturare le eccezioni e registrare gli errori in modo appropriato.


// 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 reindirizza a una pagina di errore
 }
}

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

Best Practice

Risoluzione dei Problemi Comuni

Conclusione

Il middleware di Next.js è uno strumento potente per creare applicazioni web dinamiche e personalizzate. Padroneggiando l'intercettazione delle richieste, puoi implementare una vasta gamma di funzionalità, dall'autenticazione e autorizzazione al reindirizzamento e A/B testing. Seguendo le best practice descritte in questa guida, puoi sfruttare il middleware di Next.js per creare applicazioni ad alte prestazioni, sicure e scalabili che soddisfino le esigenze della tua base di utenti globale. Sfrutta la potenza del middleware per sbloccare nuove possibilità nei tuoi progetti Next.js e offrire esperienze utente eccezionali.

Middleware di Next.js: Padroneggiare l'intercettazione delle richieste per applicazioni dinamiche | MLOG