Aller au contenu

ISR avec Strapi & Next.js : guide pour une génération statique

Apprenez à combiner l’Incremental Static Regeneration de Next.js avec le plugin Revisionary de Strapi pour servir du contenu à jour, respecter les verrous éditoriaux et garder l’historique complet des versions.

Par l’équipe Revisionary · · 5 min de lecture

laptop screen displaying colorful code
Photo de Mohammad Rahmani sur Unsplash

L’Incremental Static Regeneration (ISR) permet à un site Next.js de servir des pages pré‑rendu et de les mettre à jour en arrière‑plan sans reconstruction complète. Lorsque la source de données est un CMS headless comme Strapi, il faut synchroniser le déclenchement de la régénération avec le flux éditorial afin que les brouillons non publiés, les verrous d’édition ou les restaurations de contenu ne soient pas servies accidentellement. Ce guide explique comment connecter l’ISR au plugin Revisionary de Strapi, récupérer uniquement les versions publiées et garder l’historique complet des versions disponible pour l’audit et le rollback.

1. Prérequis

ExigenceDétails
Instance StrapiStrapi en cours d’exécution avec le plugin Revisionary installé (voir le guide Install)
Projet Next.jsUtilisant TypeScript, Node.js LTS et le package next
Accès APIEndpoint Content API activé, avec un token d’authentification en lecture‑seule
DéploiementVercel, Netlify ou toute plateforme respectant les en‑têtes ISR de Next.js

Assurez‑vous que le plugin Revisionary est actif afin que chaque modification de contenu soit enregistrée. Le plugin expose l’historique des versions via l’API Content standard, ce qui permet au front‑end de demander la version publiée tout en pouvant interroger les versions passées pour le débogage ou la prévisualisation.

Employer dashboard showing application trends and key metrics
Photo de prashant hiremath sur Unsplash

2. Récupérer uniquement la version publiée

Strapi conserve les brouillons, les publications planifiées et les instantanés historiques créés par Revisionary. Pour éviter de servir un brouillon, interrogez l’API avec le filtre publicationState réglé sur live.

ts
// lib/strapi.ts
import fetch from 'node-fetch'

const API_URL = process.env.STRAPI_URL!
const API_TOKEN = process.env.STRAPI_TOKEN!

export async function fetchLive<T>(endpoint: string): Promise<T> {
  const url = new URL(`${API_URL}/api/${endpoint}`)
  // `publicationState=live` force Strapi à ne renvoyer que les entrées publiées
  url.searchParams.append('publicationState', 'live')
  const res = await fetch(url.toString(), {
    headers: { Authorization: `Bearer ${API_TOKEN}` },
  })
  if (!res.ok) {
    throw new Error(`Strapi request failed: ${res.status}`)
  }
  const { data } = await res.json()
  return data as T
}

Toutes les fonctions de récupération de données au niveau des pages doivent utiliser fetchLive afin que la revalidation ISR récupère toujours le même instantané stable que les rédacteurs ont publié.

3. Ajouter l’ISR à un composant de page

Next.js prend en charge l’ISR via la propriété revalidate renvoyée par getStaticProps. Combinez‑la avec la récupération en direct de l’étape précédente.

tsx
// pages/[slug].tsx
import { GetStaticPaths, GetStaticProps } from 'next'
import { fetchLive } from '@/lib/strapi'
import type { Article } from '@/types'

export const getStaticPaths: GetStaticPaths = async () => {
  const articles = await fetchLive<Article[]>('articles')
  const paths = articles.map(a => ({ params: { slug: a.attributes.slug } }))
  return { paths, fallback: 'blocking' }
}

export const getStaticProps: GetStaticProps = async ({ params }) => {
  const slug = params?.slug as string
  const article = await fetchLive<Article>(`articles?filters[slug][$eq]=${slug}`)
  if (!article) return { notFound: true }

  return {
    props: { article },
    // Regénérer la page au maximum une fois toutes les 60 secondes
    revalidate: 60,
  }
}

export default function ArticlePage({ article }: { article: Article }) {
  return (
    <article>
      <h1>{article.attributes.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: article.attributes.body }} />
    </article>
  )
}

La ligne revalidate: 60 indique à Next.js de garder la page à jour pendant jusqu’à une minute. Ajustez l’intervalle en fonction du rythme éditorial.

4. Respecter les verrous d’édition et les revues

Revisionary introduit des verrous d’édition pour empêcher deux rédacteurs de s’écraser mutuellement ainsi qu’un espace Review pour le contenu en attente d’approbation. Lorsqu’une entrée est verrouillée ou en revue, vous ne devez pas déclencher de régénération. Laissez le verrou en place jusqu’à ce que le relecteur publie.

4.1 Détecter un verrou via l’API

Revisionary ajoute un drapeau locked et un champ reviewStatus aux métadonnées de l’entrée. Interrogez ces champs en même temps que le contenu habituel.

ts
export async function fetchArticleMeta(slug: string) {
  const url = new URL(`${API_URL}/api/articles`)
  url.searchParams.append('filters[slug][$eq]', slug)
  url.searchParams.append('fields', 'locked,reviewStatus')
  url.searchParams.append('publicationState', 'live')
  const res = await fetch(url.toString(), {
    headers: { Authorization: `Bearer ${API_TOKEN}` },
  })
  const { data } = await res.json()
  return data[0]?.attributes ?? null
}

4.2 Revalidation conditionnelle

Next.js vous permet de revalider programmatiquement un chemin depuis une route API. Combinez la vérification du verrou avec un webhook que Strapi déclenche à la publication.

ts
// pages/api/revalidate.ts
import type { NextApiRequest, NextApiResponse } from 'next'
import { fetchArticleMeta } from '@/lib/strapi'

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const token = req.headers['x-webhook-token']
  if (token !== process.env.STRAPI_WEBHOOK_TOKEN) {
    return res.status(401).end('Invalid token')
  }

  const { slug } = req.body
  const meta = await fetchArticleMeta(slug)
  if (meta.locked || meta.reviewStatus !== 'approved') {
    // Ignorer la revalidation ; le contenu n’est pas prêt à être consommé publiquement
    return res.status(200).json({ revalidated: false, reason: 'locked or pending review' })
  }

  try {
    await res.revalidate(`/${slug}`)
    return res.json({ revalidated: true })
  } catch (err) {
    return res.status(500).json({ error: 'Revalidation failed' })
  }
}

Configurez Strapi pour appeler cet endpoint lors de l’événement du cycle de vie afterPublish. Le payload du webhook doit contenir le slug de l’entrée publiée.

5. Exploiter l’historique des versions pour la prévisualisation

Même si l’ISR ne sert que la version live, vous pouvez toujours proposer un mode prévisualisation qui lit une révision spécifique. Revisionary stocke chaque version sous une relation versions ; l’API Content peut la récupérer avec une requête populate[versions].

ts
export async function fetchVersion(slug: string, versionId: number) {
  const url = new URL(`${API_URL}/api/articles`)
  url.searchParams.append('filters[slug][$eq]', slug)
  url.searchParams.append('populate[versions][filters][id][$eq]', versionId.toString())
  const res = await fetch(url.toString(), {
    headers: { Authorization: `Bearer ${API_TOKEN}` },
  })
  const { data } = await res.json()
  return data[0]?.attributes?.versions?.data[0]?.attributes ?? null
}

Exposez une route de prévisualisation qui définit previewData (mode preview de Next.js) et rend la version sélectionnée. Cela permet aux rédacteurs de voir exactement ce qui sera publié sans affecter le cache ISR.

6. Intégration CI/CD

  1. Lint & type‑check – Exécutez eslint et tsc à la fois sur le dossier des plugins Strapi et le code Next.js.
  2. Test du schéma – Utilisez un script qui interroge l’endpoint schema de l’API Content et vérifie que les champs requis existent.
  3. Test du verrou de version – Simulez un verrou d’édition de brouillon via l’API et vérifiez que le webhook de revalidation renvoie revalidated: false.
  4. Build – Compilez l’application Next.js (next build). La compilation échouera si un getStaticProps lève une exception à cause de l’absence de données live.
  5. Déploiement – Déployez sur Vercel (ou votre hôte). Vercel respecte automatiquement les intervalles revalidate.
yaml
name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Node
        uses: actions/setup-node@v3
        with:
          node-version: '20'
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - name: Verify Strapi schema
        env:
          STRAPI_URL: ${{ secrets.STRAPI_URL }}
          STRAPI_TOKEN: ${{ secrets.STRAPI_TOKEN }}
        run: node scripts/verify-schema.js
      - run: npm run build

7. Observabilité et journalisation

Revisionary consigne chaque verrou, restauration et action de suppression dans le audit log. Transférez ces journaux vers votre plateforme d’observabilité préférée (par ex., Loki, Datadog) en activant le transport logger de Strapi. Combinez le journal d’audit avec les journaux de requêtes Next.js pour tracer le chemin complet depuis l’action d’un rédacteur jusqu’au moment où la page est régénérée.

js
// config/logger.js (Strapi)
module.exports = ({ env }) => ({
  transports: [
    {
      level: 'info',
      transport: {
        target: 'pino-pretty',
        options: { destination: './logs/combined.log' },
      },
    },
    // Ajouter un transport HTTP pour envoyer les journaux vers un système externe
    {
      level: 'warn',
      transport: {
        target: 'pino-http',
        options: { url: env('LOGGING_ENDPOINT') },
      },
    },
  ],
})

Surveiller les champs locked et reviewStatus en temps réel vous aide à détecter quand un gel de contenu empêche involontairement les mises à jour ISR.

8. Rassembler le tout

  1. Configurer Revisionary – Choisissez quels types de contenu sont versionnés via la page Choose what is versioned.
  2. Exposer l’API live – Utilisez le filtre publicationState=live dans chaque récupération Next.js.
  3. Configurer le webhook – Dirigez le webhook afterPublish de Strapi vers /api/revalidate.
  4. Implémenter la vérification du verrou – Le gestionnaire de revalidation ignore les pages encore en cours d’édition ou de révision.
  5. Ajouter la prévisualisation – Utilisez le endpoint d’historique des versions pour rendre les brouillons aux relecteurs internes.
  6. Automatiser CI/CD – Exécutez lint, type‑check, vérification du schéma et étapes de build à chaque commit.
  7. Journaliser et observer – Transférez les logs d’audit Revisionary et les logs de requêtes Next.js vers un outil d’observabilité central.

Suivre ces étapes vous fournit un front‑end rapide et cache‑friendly qui ne fuit jamais les brouillons non publiés, tout en préservant la traçabilité éditoriale complète offerte par Revisionary.

9. Pièges courants

SymptômeCause probableSolution
L’ISR sert du contenu périmé après une publicationIncohérence du token du webhookAssurez‑vous que STRAPI_WEBHOOK_TOKEN correspond au token configuré dans Strapi
404 sur l’URL de prévisualisationDrapeau previewData manquantAppelez res.setPreviewData({}) avant de rendre la page de prévisualisation
Journal d’audit vide pour les verrousRevisionary non activé sur le type de contenuActivez le versionnage pour le type dans Choose what is versioned
Boucle de revalidation (reconstructions continues)publicationState omis, récupération des brouillonsAjoutez toujours publicationState=live aux appels API
a rack of servers in a server room
Photo de Kevin Ache sur Unsplash

10. Conclusion

L’Incremental Static Regeneration vous offre les performances des pages statiques tout en gardant votre site à jour. Lorsque la source de données est un CMS headless sensible aux versions comme Strapi, l’étape supplémentaire consiste à respecter les états éditoriaux — brouillons, verrous et revues — afin que seul le contenu approuvé atteigne le cache. En filtrant sur l’état de publication live, en vérifiant les métadonnées de verrou de Revisionary et en connectant un webhook qui déclenche conditionnellement la revalidation, vous obtenez un workflow sûr, observable et prêt pour le CI. La même API Content vous permet également de créer des expériences de prévisualisation qui récupèrent n’importe quelle version historique, offrant aux rédacteurs la confiance que leurs modifications sont correctement capturées avant la mise en ligne.

Mots-clésNext.jsISRStrapiRevisionaryVersionnageTypeScript

L’équipe Revisionary

Les développeurs de Revisionary, un plugin d’historique des versions pour Strapi 5. Nous écrivons sur Strapi, le développement web, l’IA et la sécurité des applications.

Cet article a été rédigé avec l'aide d'une IA.

Tous les articles