Articles
Apprendre Le Web
< Retour
27/11/2023
Récupération de Données, Mise en Cache et Revalidation dans React et Next.js

Récupération de Données, Mise en Cache et Revalidation dans React et Next.js

La récupération de données est un aspect essentiel du développement d'applications web modernes. Dans ce guide, nous explorerons comment récupérer, mettre en cache et revalider efficacement les données dans le contexte de React et Next.js. Ces techniques sont cruciales pour offrir des expériences web rapides et réactives à vos utilisateurs.

Récupération de Données : Quatre Approches

Il existe quatre principales méthodes pour récupérer des données lorsque vous travaillez avec React et Next.js :

  1. Sur le serveur, avec fetch : Vous pouvez récupérer des données côté serveur en utilisant l'API native fetch. Cette approche est couramment utilisée dans les Server Components, les Route Handlers et les Server Actions.

  2. Sur le serveur, avec des bibliothèques tierces : Si vous utilisez des bibliothèques tierces qui ne prennent pas en charge nativement fetch, vous pouvez toujours configurer la mise en cache et la revalidation côté serveur.

  3. Côté client, via un Gestionnaire de Routes : Dans certains cas, vous devrez peut-être récupérer des données côté client sans exposer d'informations sensibles au client, comme des tokens d'API. Les Gestionnaires de Routes s'exécutent côté serveur et renvoient des données au client.

  4. Côté client, avec des bibliothèques tierces : Vous pouvez également récupérer des données côté client en utilisant une bibliothèque tierce telle que SWR ou React Query. Ces bibliothèques offrent leurs propres API pour la mémorisation des requêtes, la mise en cache, la revalidation et la mutation des données.

Récupération de Données sur le Serveur avec fetch

Next.js étend l'API Web native fetch pour permettre la configuration de la mise en cache et du comportement de revalidation pour chaque requête fetch côté serveur. De plus, React étend fetch pour mémoriser automatiquement les requêtes fetch lors du rendu d'un arbre de composants React.

async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
        throw new Error('Échec de la récupération des données');
    }
    return response.json();
}

export default async function Page() {
    const data = await fetchData();
    return <main>{/* Rendu de vos données ici */}</main>;
}

Points importants à retenir :

  • Next.js fournit des fonctions utiles que vous pouvez utiliser lors de la récupération de données dans les Server Components, notamment l'accès aux cookies et aux en-têtes. Ces fonctions entraîneront un rendu dynamique de la route car elles dépendent des informations relatives à la requête.
  • Les requêtes fetch dans les Gestionnaires de Routes ne sont pas mémorisées car les Gestionnaires de Routes ne font pas partie de l'arbre de composants React.
  • Pour utiliser async/await dans un Server Component avec TypeScript, assurez-vous d'utiliser TypeScript 5.1.3 ou supérieur et @types/react 18.2.8 ou supérieur.

Mise en Cache des Données

La mise en cache consiste à stocker des données pour éviter de les récupérer à chaque nouvelle requête. Par défaut, Next.js met automatiquement en cache les résultats des requêtes fetch dans la Data Cache côté serveur. Cela signifie que les données peuvent être récupérées au moment de la construction ou au moment de la requête, puis mises en cache et réutilisées à chaque nouvelle demande de données.

Cache de Données

La mise en cache de données consiste à stocker temporairement des informations pour éviter de les récupérer à partir de la source de données à chaque requête. Par défaut, Next.js gère automatiquement le cache des valeurs renvoyées par les requêtes fetch dans le Data Cache côté serveur. Cela signifie que les données peuvent être récupérées au moment de la construction ou au moment de la requête, puis mises en cache et réutilisées à chaque demande ultérieure de données.

// 'force-cache' est la valeur par défaut et peut être omise
fetch('https://...', { cache: 'force-cache' });

Les requêtes fetch utilisant la méthode POST sont également automatiquement mises en cache, sauf si elles sont effectuées dans un Gestionnaire de Routes qui utilise la méthode POST, auquel cas elles ne seront pas mises en cache.

Qu'est-ce que le Data Cache ?

Le Data Cache est un cache HTTP persistant. Selon votre plateforme, le cache peut s'adapter automatiquement et être partagé entre plusieurs régions.

Apprenez-en davantage sur le Data Cache.

Revalidation de Données

La revalidation est le processus de purification du Data Cache et de récupération des données les plus récentes. Cela est utile lorsque vos données changent et que vous souhaitez vous assurer d'afficher les dernières informations.

Les données mises en cache peuvent être revalidées de deux manières :

Revalidation basée sur le temps : Revalidez automatiquement les données après un certain laps de temps. Cela convient aux données qui changent rarement et pour lesquelles la fraîcheur n'est pas essentielle.

fetch('https://...', { next: { revalidate: 3600 } });

Alternativement, pour revalider l'ensemble des requêtes fetch dans un segment de route, vous pouvez utiliser les Options de Configuration de Segment.

// layout.js ou page.js
export const revalidate = 3600; // Revalider au plus une fois par heure

Lorsque vous avez plusieurs requêtes fetch dans une route rendue statiquement, la durée de revalidation la plus courte sera utilisée pour toutes les requêtes. Pour les routes rendues dynamiquement, chaque requête fetch est revalidée indépendamment.

En savoir plus sur la revalidation basée sur le temps.

Revalidation sur Demande

Les données peuvent être révalidées sur demande, soit par chemin (revalidatePath), soit par balise de cache (revalidateTag), à l'intérieur d'un Gestionnaire de Routes ou d'une Action Serveur.

Next.js dispose d'un système de balisage de cache pour invalider les requêtes fetch à travers les routes.

Lorsque vous utilisez fetch, vous avez la possibilité d'ajouter des balises de cache aux entrées de cache.

// app/page.tsx


export default async function Page() {
    const response = await fetch('https://...', { next: { tags: ['collection'] } });
    const data = await response.json();
    // ...
}

Lors de l'utilisation d'un Gestionnaire de Routes, vous devriez créer un token secret uniquement connu de votre application Next.js. Ce secret sera utilisé pour empêcher les tentatives de révalidation non autorisées.

Par exemple, vous pouvez accéder à la route (manuellement ou avec un webhook) avec la structure d'URL suivante :

URL

https://*votre-site.com*/api/revalidate?tag=collection&secret=*token*

app/api/revalidate/route.ts

// app/api/revalidate/route.ts
import { NextRequest } from 'next/server';
import { revalidateTag } from 'next/cache';

// Par exemple, un webhook vers `votre-site.com/api/revalidate?tag=collection&secret=*token*`
export async function POST(request: NextRequest) {
    const secret = request.nextUrl.searchParams.get('secret');
    const tag = request.nextUrl.searchParams.get('tag');

    if (secret !== process.env.MY_SECRET_TOKEN) {
        return Response.json({ message: 'Secret invalide' }, { status: 401 });
    }

    if (!tag) {
        return Response.json({ message: 'Paramètre de balise manquant' }, { status: 400 });
    }

    revalidateTag(tag);

    return Response.json({ révalidé : vrai, maintenant : Date.now() });
}

En alternance, vous pouvez utiliser revalidatePath pour révalider toutes les données associées à un chemin.

app/api/revalidate/route.ts

// app/api/revalidate/route.ts
import { NextRequest } from 'next/server';
import { revalidatePath } from 'next/cache';

export async function POST(request: NextRequest) {
    const path = request.nextUrl.searchParams.get('path');

    if (!path) {
        return Response.json({ message: 'Paramètre de chemin manquant' }, { status: 400 });
    }

    revalidatePath(path);

    return Response.json({ révalidé : vrai, maintenant : Date.now() });
}

En savoir plus sur la révalidation sur demande.

Gestion des Erreurs et Revalidation

Si une erreur se produit lors de la tentative de révalidation des données, les dernières données générées avec succès continueront d'être servies à partir du cache. Lors de la prochaine demande, Next.js réessayera de révalider les données.

Exclusion de la Mise en Cache de Données

Les requêtes fetch ne sont pas mises en cache dans les cas suivants :

  • La valeur cache: 'no-store' est ajoutée aux requêtes fetch.
  • L'option revalidate: 0 est ajoutée aux requêtes fetch individuelles.
  • La requête fetch est effectuée à l'intérieur d'un Gestionnaire de Routes utilisant la méthode POST.
  • La requête fetch intervient après l'utilisation des en-têtes (headers) ou des cookies.
  • Le segment de route contient l'option const dynamic = 'force-dynamic', utilisée pour une mise en cache dynamique.
  • Le segment de route est configuré pour ignorer la mise en cache par défaut en utilisant l'option fetchCache.
  • La requête fetch utilise des en-têtes d'autorisation ou de cookies, et qu'il y a une requête non mise en cache au-dessus d'elle dans l'arborescence des composants.

Requêtes fetch Individuelles

Pour exclure des requêtes fetch individuelles de la mise en cache, vous pouvez définir l'option cache sur 'no-store'. Cela forcera la récupération dynamique des données à chaque nouvelle demande.

// layout.js ou page.js
fetch('https://...', { cache: 'no-store' });

Consultez toutes les options de mise en cache disponibles dans la référence de l'API fetch.

Plusieurs Requêtes fetch

Si vous avez plusieurs requêtes fetch dans un segment de route (par exemple, une Mise en Page ou une Page), vous pouvez configurer le comportement de mise en cache de l'ensemble des requêtes de données dans le segment en utilisant les Options de Configuration de Segment.

Par exemple, en utilisant const dynamic = 'force-dynamic', vous forcerez la récupération de toutes les données au moment de la demande, et le segment sera rendu de manière dynamique.

// layout.js ou page.js
export const dynamic = 'force-dynamic';

Il existe une liste étendue d'options de configuration de segment, offrant un contrôle précis sur le comportement statique et dynamique d'un segment de route. Consultez la référence de l'API pour en savoir plus.

Récupération de Données sur le Serveur avec des Bibliothèques Tierces

Dans les cas où vous utilisez une bibliothèque tierce qui ne prend pas en charge ou n'expose pas nativement fetch (par exemple, une base de données, un CMS ou un client ORM), vous pouvez configurer la mise en cache et la revalidation des requêtes à l'aide de l'Option de Configuration de Segment de Route et de la fonction de cache de React.

Que les données soient mises en cache ou non dépendra de la manière dont le segment de route est rendu, statiquement ou dynamiquement. Si le segment est statique (par défaut), le résultat de la requête sera mis en cache et révalidé en même temps que le segment de route. Si le segment est dynamique, le résultat de la requête ne sera pas mis en cache et sera récupéré à chaque nouvelle demande lorsque le segment est rendu.

Points importants à noter :

  • Next.js travaille sur une API, unstable_cache, pour configurer la mise en cache et la revalidation du comportement de requêtes tierces individuelles.

Exemple

Dans l'exemple suivant :

  • L'option de revalidation est définie sur 3600, ce qui signifie que les données seront mises en cache et révalidées au maximum toutes les heures.
  • La fonction de cache de React est utilisée pour mémoriser les requêtes de données.
// utils/get-item.ts
import { cache } from 'react';

export const revalidate = 3600 // Révalider au maximum toutes les heures

export const getItem = cache(async (id: string) => {
    const item = await db.item.findUnique({ id });
    return item;
});

La fonction getItem est appelée deux fois, mais une seule requête sera effectuée vers la base de données.

// app/item/[id]/layout.tsx
import { getItem } from '@/utils/get-item'

export default async function Layout({
    params: { id },
}: {
    params: { id: string }
}) {
    const item = await getItem(id)
    // ...
}
// app/item/[id]/page.tsx
import { getItem } from '@/utils/get-item'

export default async function Page({
    params: { id },
}: {
    params: { id: string }
}) {
    const item = await getItem(id)
    // ...
}

Récupération de Données sur le Client avec des Gestionnaires de Routes

Si vous avez besoin de récupérer des données dans un composant client, vous pouvez appeler un Gestionnaire de Routes depuis le client. Les Gestionnaires de Routes s'exécutent sur le serveur et renvoient les données au client. Cela est utile lorsque vous ne souhaitez pas exposer des informations sensibles au client, telles que des tokens d'API.

Consultez la documentation sur les Gestionnaires de Routes pour des exemples.

Composants Serveur et Gestionnaires de Routes

Comme les Composants Serveur rendent sur le serveur, vous n'avez pas besoin d'appeler un Gestionnaire de Routes depuis un Composant Serveur pour récupérer des données. Au lieu de cela, vous pouvez récupérer les données directement à l'intérieur du Composant Serveur.

Récupération de Données sur le Client avec des Bibliothèques Tierces

Vous pouvez également récupérer des données sur le client en utilisant une bibliothèque tierce telle que SWR ou React Query. Ces bibliothèques fournissent leurs propres API pour mémoriser les requêtes, mettre en cache, révalider et muter les données.