Régénération statique incrémentielle (ISR)

Exemples

Next.js vous permet de créer ou mettre à jour des pages statiques après avoir construit votre site. La régénération statique incrémentielle (ISR) vous permet d'utiliser la génération statique page par page, sans avoir à reconstruire tout le site. Avec ISR, vous conservez les avantages du statique tout en passant à l'échelle de millions de pages.

Bon à savoir : Le runtime edge n'est actuellement pas compatible avec ISR, bien que vous puissiez exploiter stale-while-revalidate en définissant manuellement l'en-tête cache-control.

Pour utiliser ISR, ajoutez la propriété revalidate à getStaticProps :

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

// Cette fonction est appelée au moment de la construction côté serveur.
// Elle peut être rappelée, sur une fonction serverless, si
// la revalidation est activée et qu'une nouvelle requête arrive
export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // Next.js tentera de régénérer la page :
    // - Lorsqu'une requête arrive
    // - Au maximum une fois toutes les 10 secondes
    revalidate: 10, // En secondes
  }
}

// Cette fonction est appelée au moment de la construction côté serveur.
// Elle peut être rappelée, sur une fonction serverless, si
// le chemin n'a pas été généré.
export async function getStaticPaths() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  // Obtenez les chemins que nous voulons pré-rendre basés sur les posts
  const paths = posts.map((post) => ({
    params: { id: post.id },
  }))

  // Nous pré-rendrons uniquement ces chemins au moment de la construction.
  // { fallback: 'blocking' } rendra les pages côté serveur
  // à la demande si le chemin n'existe pas.
  return { paths, fallback: 'blocking' }
}

export default Blog

Lorsqu'une requête est faite vers une page pré-rendue au moment de la construction, elle affichera d'abord la page mise en cache.

  • Toutes les requêtes vers la page après la requête initiale et avant 10 secondes sont également mises en cache et instantanées.
  • Après la fenêtre de 10 secondes, la prochaine requête affichera toujours la page mise en cache (obsolète)
  • Next.js déclenche une régénération de la page en arrière-plan.
  • Une fois la page générée avec succès, Next.js invalidera le cache et affichera la page mise à jour. Si la régénération en arrière-plan échoue, l'ancienne page restera inchangée.

Lorsqu'une requête est faite vers un chemin qui n'a pas été généré, Next.js rendra la page côté serveur lors de la première requête. Les requêtes futures serviront le fichier statique depuis le cache. ISR sur Vercel persiste le cache globalement et gère les rollbacks.

Bon à savoir : Vérifiez si votre fournisseur de données en amont a activé le cache par défaut. Vous devrez peut-être le désactiver (par exemple useCdn: false), sinon une revalidation ne pourra pas récupérer de nouvelles données pour mettre à jour le cache ISR. Le cache peut se produire sur un CDN (pour un point de terminaison demandé) lorsqu'il renvoie l'en-tête Cache-Control.

Revalidation à la demande

Si vous définissez un temps revalidate de 60, tous les visiteurs verront la même version générée de votre site pendant une minute. La seule façon d'invalider le cache est que quelqu'un visite cette page après que la minute soit passée.

À partir de la version v12.2.0, Next.js prend en charge la régénération statique incrémentielle à la demande pour purger manuellement le cache Next.js d'une page spécifique. Cela facilite la mise à jour de votre site lorsque :

  • Du contenu de votre CMS headless est créé ou mis à jour
  • Les métadonnées e-commerce changent (prix, description, catégorie, avis, etc.)

Dans getStaticProps, vous n'avez pas besoin de spécifier revalidate pour utiliser la revalidation à la demande. Si revalidate est omis, Next.js utilisera la valeur par défaut false (pas de revalidation) et ne revalidera la page qu'à la demande lorsque revalidate() est appelé.

Bon à savoir : Le Middleware ne sera pas exécuté pour les requêtes ISR à la demande. À la place, appelez revalidate() sur le chemin exact que vous souhaitez revalider. Par exemple, si vous avez pages/blog/[slug].js et une réécriture de /post-1 vers /blog/post-1, vous devrez appeler res.revalidate('/blog/post-1').

Utilisation de la revalidation à la demande

D'abord, créez un jeton secret connu uniquement par votre application Next.js. Ce secret sera utilisé pour empêcher l'accès non autorisé à la route API de revalidation. Vous pouvez accéder à la route (manuellement ou avec un webhook) avec la structure d'URL suivante :

Terminal
https://<your-site.com>/api/revalidate?secret=<token>

Ensuite, ajoutez le secret comme variable d'environnement à votre application. Enfin, créez la route API de revalidation :

pages/api/revalidate.js
export default async function handler(req, res) {
  // Vérifiez le secret pour confirmer que c'est une requête valide
  if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
    return res.status(401).json({ message: 'Token invalide' })
  }

  try {
    // ce doit être le chemin réel et non un chemin réécrit
    // par exemple pour "/blog/[slug]" ce doit être "/blog/post-1"
    await res.revalidate('/path-to-revalidate')
    return res.json({ revalidated: true })
  } catch (err) {
    // S'il y a une erreur, Next.js continuera
    // à afficher la dernière page générée avec succès
    return res.status(500).send('Erreur lors de la revalidation')
  }
}

Voir notre démo pour voir la revalidation à la demande en action et donner votre feedback.

Tester ISR à la demande pendant le développement

Lors de l'exécution locale avec next dev, getStaticProps est invoqué à chaque requête. Pour vérifier que votre configuration ISR à la demande est correcte, vous devrez créer une version de production et démarrer le serveur de production :

Terminal
$ next build
$ next start

Ensuite, vous pouvez confirmer que les pages statiques ont été revalidées avec succès.

Gestion des erreurs et revalidation

S'il y a une erreur dans getStaticProps lors de la régénération en arrière-plan, ou si vous lancez une erreur manuellement, la dernière page générée avec succès continuera d'être affichée. Lors de la prochaine requête, Next.js réessayera d'appeler getStaticProps.

export async function getStaticProps() {
  // Si cette requête lance une erreur non capturée, Next.js ne
  // invalidera pas la page actuellement affichée et
  // réessayera getStaticProps lors de la prochaine requête.
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  if (!res.ok) {
    // S'il y a une erreur serveur, vous pourriez vouloir
    // lancer une erreur au lieu de retourner pour que le cache ne soit pas mis à jour
    // jusqu'à la prochaine requête réussie.
    throw new Error(`Échec de la récupération des posts, statut reçu ${res.status}`)
  }

  // Si la requête a réussi, retournez les posts
  // et revalidez toutes les 10 secondes.
  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}

Auto-hébergement ISR

La régénération statique incrémentielle (ISR) fonctionne sur les sites Next.js auto-hébergés dès le départ lorsque vous utilisez next start.

Vous pouvez utiliser cette approche lors d'un déploiement sur des orchestrateurs de conteneurs comme Kubernetes ou HashiCorp Nomad. Par défaut, les assets générés seront stockés en mémoire sur chaque pod. Cela signifie que chaque pod aura sa propre copie des fichiers statiques. Des données obsolètes peuvent être affichées jusqu'à ce que ce pod spécifique soit touché par une requête.

Pour garantir la cohérence entre tous les pods, vous pouvez désactiver le cache en mémoire. Cela indiquera au serveur Next.js de n'utiliser que les assets générés par ISR dans le système de fichiers.

Vous pouvez utiliser un montage réseau partagé dans vos pods Kubernetes (ou une configuration similaire) pour réutiliser le même cache de système de fichiers entre différents conteneurs. En partageant le même montage, le dossier .next qui contient le cache next/image sera également partagé et réutilisé.

Pour désactiver le cache en mémoire, définissez isrMemoryCacheSize à 0 dans votre fichier next.config.js :

next.config.js
module.exports = {
  experimental: {
    // Par défaut à 50MB
    isrMemoryCacheSize: 0, // taille du cache en octets
  },
}

Bon à savoir : Vous pourriez avoir besoin de considérer une condition de course entre plusieurs pods essayant de mettre à jour le cache en même temps, selon la configuration de votre montage partagé.

Historique des versions

VersionChangements
v12.2.0ISR à la demande est stable
v12.1.0ISR à la demande ajouté (bêta).
v12.0.0Fallback ISR conscient des bots ajouté.
v9.5.0Base Path ajouté.