Français

Découvrez le middleware Next.js, une fonctionnalité puissante pour intercepter et modifier les requêtes entrantes. Apprenez comment implémenter l'authentification, l'autorisation, la redirection et les tests A/B avec des exemples pratiques.

Middleware Next.js : Maîtriser l'interception de requêtes pour des applications dynamiques

Le middleware Next.js offre un moyen flexible et puissant d'intercepter et de modifier les requêtes entrantes avant qu'elles n'atteignent vos routes. Cette capacité vous permet de mettre en œuvre un large éventail de fonctionnalités, de l'authentification et l'autorisation à la redirection et aux tests A/B, tout en optimisant les performances. Ce guide complet vous présentera les concepts fondamentaux du middleware Next.js et vous montrera comment l'exploiter efficacement.

Qu'est-ce que le middleware Next.js ?

Dans Next.js, un middleware est une fonction qui s'exécute avant qu'une requête ne soit complétée. Il vous permet de :

Les fonctions de middleware sont définies dans le fichier middleware.ts (ou middleware.js) à la racine de votre projet. Elles sont exécutées pour chaque route de votre application, ou pour des routes spécifiques basées sur des matchers configurables.

Concepts clés et avantages

Objet Request

L'objet request donne accès aux informations sur la requête entrante, notamment :

Objet Response

Les fonctions de middleware retournent un objet Response pour contrôler le résultat de la requête. Vous pouvez utiliser les réponses suivantes :

Matchers

Les matchers vous permettent de spécifier à quelles routes votre middleware doit s'appliquer. Vous pouvez définir des matchers en utilisant des expressions régulières ou des modèles de chemin. Cela garantit que votre middleware ne s'exécute que lorsque c'est nécessaire, améliorant ainsi les performances et réduisant la surcharge.

Edge Runtime

Le middleware Next.js s'exécute sur l'Edge Runtime, un environnement d'exécution JavaScript léger qui peut être déployé à proximité de vos utilisateurs. Cette proximité minimise la latence et améliore les performances globales de votre application, en particulier pour les utilisateurs répartis dans le monde entier. L'Edge Runtime est disponible sur le Edge Network de Vercel et d'autres plateformes compatibles. L'Edge Runtime a certaines limitations, notamment l'utilisation des API Node.js.

Exemples pratiques : Implémentation de fonctionnalités de middleware

1. Authentification

Un middleware d'authentification peut être utilisé pour protéger les routes qui nécessitent que les utilisateurs soient connectés. Voici un exemple de mise en œuvre de l'authentification à l'aide de 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*'],
}

Ce middleware vérifie la présence d'un cookie auth_token. Si le cookie n'est pas trouvé, l'utilisateur est redirigé vers la page /login. Le config.matcher spécifie que ce middleware ne doit s'exécuter que pour les routes sous /dashboard.

Perspective globale : Adaptez la logique d'authentification pour prendre en charge diverses méthodes d'authentification (par ex., OAuth, JWT) et intégrez-la avec différents fournisseurs d'identité (par ex., Google, Facebook, Azure AD) pour répondre aux besoins des utilisateurs de diverses régions.

2. Autorisation

Un middleware d'autorisation peut être utilisé pour contrôler l'accès aux ressources en fonction des rôles ou des permissions des utilisateurs. Par exemple, vous pourriez avoir un tableau de bord administrateur accessible uniquement à des utilisateurs spécifiques.


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

 // Exemple : Récupérer les rôles de l'utilisateur depuis une API (remplacez par votre logique réelle)
 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*'],
}

Ce middleware récupère le rôle de l'utilisateur et vérifie s'il a le rôle admin. Sinon, il est redirigé vers une page /unauthorized. Cet exemple utilise un point de terminaison d'API factice. Remplacez `https://api.example.com/userinfo` par le point de terminaison réel de votre serveur d'authentification.

Perspective globale : Soyez attentif aux réglementations sur la protection des données (par ex., RGPD, CCPA) lors du traitement des données des utilisateurs. Mettez en œuvre des mesures de sécurité appropriées pour proteger les informations sensibles et garantir la conformité avec les lois locales.

3. Redirection

Un middleware de redirection peut être utilisé pour rediriger les utilisateurs en fonction de leur emplacement, de leur langue ou d'autres critères. Par exemple, vous pourriez rediriger les utilisateurs vers une version localisée de votre site web en fonction de leur adresse 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'; // Par défaut, US si la géolocalisation échoue

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

Ce middleware vérifie le pays de l'utilisateur en fonction de son adresse IP et le redirige vers la version localisée appropriée du site web (/de pour l'Allemagne, /fr pour la France). Si la géolocalisation échoue, il utilise par défaut la version américaine. Notez que cela dépend de la disponibilité de la propriété geo (par exemple, lors d'un déploiement sur Vercel).

Perspective globale : Assurez-vous que votre site web prend en charge plusieurs langues et devises. Donnez aux utilisateurs la possibilité de sélectionner manuellement leur langue ou leur région préférée. Utilisez des formats de date et d'heure appropriés pour chaque locale.

4. Test A/B

Le middleware peut être utilisé pour implémenter des tests A/B en assignant aléatoirement les utilisateurs à différentes variantes d'une page et en suivant leur comportement. Voici un exemple simplifié :


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

Ce middleware assigne les utilisateurs soit à la variante 'A', soit à la 'B'. Si un utilisateur n'a pas déjà de cookie variant, un est assigné aléatoirement et défini. Les utilisateurs assignés à la variante 'B' sont réécrits vers la page /variant-b. Vous suivriez ensuite les performances de chaque variante pour déterminer laquelle est la plus efficace.

Perspective globale : Tenez compte des différences culturelles lors de la conception des tests A/B. Ce qui fonctionne bien dans une région peut ne pas trouver d'écho auprès des utilisateurs d'une autre. Assurez-vous que votre plateforme de test A/B est conforme aux réglementations sur la vie privée dans différentes régions.

5. Feature Flags

Les feature flags vous permettent d'activer ou de désactiver des fonctionnalités dans votre application sans déployer de nouveau code. Le middleware peut être utilisé pour déterminer si un utilisateur doit avoir accès à une fonctionnalité spécifique en fonction de son ID utilisateur, de son emplacement ou d'autres critères.


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

export async function middleware(request: NextRequest) {
 // Exemple : Récupérer les feature flags depuis une 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) {
 // Activer la nouvelle fonctionnalité
 return NextResponse.next();
 } else {
 // Désactiver la nouvelle fonctionnalité (par ex., rediriger vers une page alternative)
 return NextResponse.redirect(new URL('/alternative-page', request.url));
 }
}

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

Ce middleware récupère les feature flags depuis une API et vérifie si le flag new_feature_enabled est activé. Si c'est le cas, l'utilisateur peut accéder à la page /new-feature. Sinon, il est redirigé vers une /alternative-page.

Perspective globale : Utilisez les feature flags pour déployer progressivement de nouvelles fonctionnalités aux utilisateurs de différentes régions. Cela vous permet de surveiller les performances et de résoudre les problèmes avant de lancer la fonctionnalité à un public plus large. Assurez-vous également que votre système de feature flagging est scalable à l'échelle mondiale et fournit des résultats cohérents quel que soit l'emplacement de l'utilisateur. Tenez compte des contraintes réglementaires régionales pour les déploiements de fonctionnalités.

Techniques avancées

Chaînage de middlewares

Vous pouvez enchaîner plusieurs fonctions de middleware pour effectuer une série d'opérations sur une requête. Cela peut être utile pour décomposer une logique complexe en modules plus petits et plus faciles à gérer.


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

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

 // Première fonction de middleware
 const token = request.cookies.get('auth_token');
 if (!token) {
 return NextResponse.redirect(new URL('/login', request.url))
 }

 // Deuxième fonction de middleware
 response.headers.set('x-middleware-custom', 'value');

 return response;
}

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

Cet exemple montre deux middlewares en un. Le premier effectue l'authentification et le second définit un en-tête personnalisé.

Utilisation des variables d'environnement

Stockez les informations sensibles, telles que les clés d'API et les identifiants de base de données, dans des variables d'environnement plutôt que de les coder en dur dans vos fonctions de middleware. Cela améliore la sécurité et facilite la gestion de la configuration de votre application.


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

Dans cet exemple, la API_KEY est récupérée à partir d'une variable d'environnement.

Gestion des erreurs

Implémentez une gestion robuste des erreurs dans vos fonctions de middleware pour éviter que des erreurs inattendues ne fassent planter votre application. Utilisez des blocs try...catch pour intercepter les exceptions et consigner les erreurs de manière appropriée.


// 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('Erreur lors de la récupération des données :', error);
 return NextResponse.error(); // Ou rediriger vers une page d'erreur
 }
}

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

Meilleures pratiques

Dépannage des problèmes courants

Conclusion

Le middleware Next.js est un outil puissant pour créer des applications web dynamiques et personnalisées. En maîtrisant l'interception de requêtes, vous pouvez implémenter une large gamme de fonctionnalités, de l'authentification et l'autorisation à la redirection et aux tests A/B. En suivant les meilleures pratiques décrites dans ce guide, vous pouvez exploiter le middleware Next.js pour créer des applications performantes, sécurisées et évolutives qui répondent aux besoins de votre base d'utilisateurs mondiale. Exploitez la puissance du middleware pour débloquer de nouvelles possibilités dans vos projets Next.js et offrir des expériences utilisateur exceptionnelles.