Aller au contenu

Automatiser CI/CD pour Strapi 5 + Next.js : Reconstruire à chaque publication avec GitHub Actions

Apprenez pas à pas à configurer les webhooks Strapi 5, un endpoint ISR on‑demand dans Next.js et un workflow GitHub Actions afin que chaque publication déclenche un nouveau build et déploiement.

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

turned on MacBook Air on desk
Photo de Goran Ivos sur Unsplash

Lorsqu’un·e rédacteur·rice de contenu publie une entrée dans Strapi 5, le site doit afficher la modification immédiatement, sans redeploiement manuel. Vous pouvez y parvenir en connectant les webhooks de Strapi à une pipeline CI/CD (GitHub Actions) qui déclenche une construction et un déploiement de Next.js. Ce guide présente la configuration complète : configuration du webhook Strapi, une API sécurisée de revalidation on‑demand dans Next.js, et un workflow GitHub Actions qui s’exécute à la réception des webhooks.

Vue d’ensemble du workflow

  1. Le webhook Strapi se déclenche sur l’événement entry.publish.
  2. Le webhook appelle une route API protégée (/api/revalidate) dans l’application Next.js.
  3. La route API invoque revalidatePath (ou revalidateTag) pour purger le cache ISR.
  4. La route déclenche également un workflow GitHub Actions via l’événement repository_dispatch.
  5. GitHub Actions récupère le dépôt, exécute npm install && npm run build && npm run start (ou déploie sur Vercel/Netlify), complétant le cycle CI/CD.

L’ensemble du processus s’exécute dans le runtime Node.js, car les runtimes Edge ne peuvent pas effectuer d’ISR on‑demand.

A computer screen with a green light on it
Photo de Milad Fakurian sur Unsplash

1. Configurer le webhook Strapi

Créez un webhook dans le panneau d’administration de Strapi (Settings → Webhooks) ou ajoutez‑le programmatiquement dans config/server.ts.

ts
// ./config/server.ts
import { defineConfig } from '@strapi/types';

export default defineConfig({
  // Paramètres globaux du webhook (optionnel)
  webhook: {
    // Ajoute un en‑tête Authorization qui sera vérifié par Next.js
    headers: {
      Authorization: 'Bearer MY_STRAPI_WEBHOOK_SECRET',
    },
    // Vérifie les signatures du payload (recommandé)
    signature: {
      secret: process.env.STRAPI_WEBHOOK_SECRET,
    },
  },
  // Enregistre le webhook de publication
  webhook: {
    enabled: true,
    events: ['entry.publish'],
    url: `${process.env.NEXT_PUBLIC_SITE_URL}/api/revalidate`,
    // Ne se déclenche que pour les types de contenu nécessitant l’ISR
    contentTypes: ['api::article.article', 'api::page.page'],
  },
});

Considérations de sécurité

  • Vérifiez l’en‑tête Authorization.
  • Validez la signature HMAC si vous activez signature dans Strapi (voir l’exemple de vérification de signature de webhook dans la documentation Strapi【3†L112-L136】).
  • Restreignez l’URL du webhook aux seules requêtes POST.

2. Créer un endpoint de revalidation protégé dans Next.js

Placez le fichier sous app/api/revalidate/route.ts (App Router) et marquez‑le comme route Node.js en ajoutant export const runtime = 'nodejs'; – cela évite la limitation du runtime Edge【8†L16-L17】.

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

export const runtime = 'nodejs'; // Garantir le runtime Node.js pour l’ISR

/**
 * Vérifier le secret envoyé par Strapi.
 */
function verifySecret(req: NextRequest): boolean {
  const auth = req.headers.get('authorization');
  return auth === `Bearer ${process.env.NEXT_WEBHOOK_SECRET}`;
}

export async function POST(req: NextRequest) {
  if (!verifySecret(req)) {
    return new NextResponse('Invalid secret', { status: 401 });
  }

  const payload = await req.json();
  const { model, uid, entry } = payload;

  // Déterminer le(s) chemin(s) à revalider.
  // Exemple : les slugs d’article sont stockés dans entry.slug
  const paths = [] as string[];
  if (model === 'api::article.article') {
    paths.push(`/articles/${entry.slug}`);
  } else if (model === 'api::page.page') {
    paths.push(`/${entry.slug}`);
  }

  try {
    // Revalider chaque chemin à la demande.
    for (const path of paths) {
      await revalidatePath(path);
    }
    // Déclencher un run GitHub Actions via repository_dispatch.
    await triggerGitHubDispatch();
    return NextResponse.json({ revalidated: true, paths });
  } catch (err) {
    console.error('Revalidation error:', err);
    return new NextResponse('Revalidation failed', { status: 500 });
  }
}

/**
 * Call the GitHub repository_dispatch endpoint.
 */
async function triggerGitHubDispatch() {
  const token = process.env.GITHUB_TOKEN; // jeton d’accès personnel avec le scope repo
  const repo = process.env.GITHUB_REPO; // par ex. "owner/repo"
  const response = await fetch(`https://api.github.com/repos/${repo}/dispatches`, {
    method: 'POST',
    headers: {
      Accept: 'application/vnd.github+json',
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      event_type: 'strapi-publish',
      client_payload: { source: 'strapi' },
    }),
  });
  if (!response.ok) {
    throw new Error('GitHub dispatch failed');
  }
}
  • revalidatePath est le helper on‑demand ISR de Next.js【6†L226-L233】.
  • La route s’exécute uniquement en Node.js, satisfaisant l’exigence de runtime.
  • La fonction triggerGitHubDispatch envoie un événement repository_dispatch que GitHub Actions peut écouter.

3. Configurer le workflow GitHub Actions

Créez le fichier .github/workflows/strapi-publish.yml dans votre dépôt.

yaml
name: Rebuild on Strapi Publish
on:
  repository_dispatch:
    types: [strapi-publish]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pages: write   # if deploying to GitHub Pages
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build Next.js app
        run: npm run build

      - name: Deploy to Vercel (or Netlify, etc.)
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
        run: |
          npm install -g vercel
          vercel --prod --token $VERCEL_TOKEN

Fonctionnement

  • Lorsque Strapi publie du contenu, le webhook appelle /api/revalidate.
  • La route API valide la requête, revalide les chemins ISR, puis envoie un événement repository_dispatch à GitHub.
  • GitHub Actions récupère l’événement strapi-publish, exécute la construction et déploie la nouvelle sortie statique.

4. Variables d’environnement

VariableOù il/elle se trouveObjectif
STRAPI_WEBHOOK_SECRET.env de StrapiSecret HMAC pour la signature du webhook
NEXT_WEBHOOK_SECRET.env.local de Next.jsMême secret utilisé pour vérifier la requête
GITHUB_TOKENSecret GitHub Actions GITHUB_TOKENJeton d’accès personnel pour repository_dispatch
GITHUB_REPOSecret GitHub Actions GITHUB_REPOIdentifiant owner/repo
VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_IDSecrets GitHub ActionsIdentifiants de déploiement Vercel

5. Tester l’intégration localement

  1. Exécuter Strapi : npm run develop (requiert Node 18‑24 et une base de données comme indiqué sur la page des prérequis)【/docs/getting-started/requirements】.
  2. Exécuter Next.js en mode Node : npm run dev et définissez NEXT_PUBLIC_SITE_URL=http://localhost:3000.
  3. Utilisez un outil tel que curl pour POST un payload de webhook factice :
    bash
    curl -X POST http://localhost:3000/api/revalidate \
         -H "Content-Type: application/json" \
         -H "Authorization: Bearer MY_STRAPI_WEBHOOK_SECRET" \
         -d '{"model":"api::article.article","uid":"article","entry":{"slug":"hello-world"}}'
  4. Vérifiez que la console affiche revalidated et que la requête de dispatch GitHub réussit (vérifiez l’onglet Actions).

6. Option : Revalidation basée sur les tags pour les pages de liste

Si vous avez une page qui liste de nombreux articles (/articles), vous pouvez taguer l’appel fetch et revalider le tag au lieu de chaque chemin individuel.

ts
// app/articles/page.tsx
export const fetchCache = 'force-no-store'; // optionnel pour des données fraîches
export const revalidate = 60; // ISR de secours

export default async function ArticlesPage() {
  const res = await fetch(`${process.env.STRAPI_API_URL}/articles`, {
    next: { tags: ['articles'] },
  });
  const data = await res.json();
  // rendre la liste …
}

Dans le gestionnaire du webhook, remplacez revalidatePath par :

ts
await revalidateTag('articles');

7. Déployer l’application Next.js

  • Vercel – le service par défaut pour Next.js. La commande CLI vercel du workflow pousse la dernière construction.
  • Netlify – utilisez le CLI Netlify (netlify deploy --prod) à la place des commandes Vercel.
  • Auto‑hébergé – si vous exécutez Next.js sur un VPS, remplacez l’étape de déploiement par un script SSH qui récupère le dépôt, exécute npm ci && npm run build && pm2 restart next.

8. Observabilité et surveillance

  • Le journal d’audit de Strapi enregistre chaque action entry.publish, vous offrant une trace immuable de qui a déclenché une reconstruction【12†L12-L13】.
  • Next.js ajoute automatiquement les en‑têtes de réponse x‑nextjs‑cache (HIT, STALE, MISS, REVALIDATED) pour vous aider à déboguer le comportement ISR【9†L23-L25】.
  • Dans GitHub Actions, activez le drapeau --debug du CLI Vercel pour capturer les logs de déploiement.

9. Pièges courants

SymptomCauseSolution
401 Invalid secret depuis /api/revalidateSecret incohérent entre Strapi et Next.jsAssurez‑vous que STRAPI_WEBHOOK_SECRET et NEXT_WEBHOOK_SECRET sont identiques et chargés depuis les fichiers d’environnement.
L’ISR ne s’exécute pas sur le runtime EdgeUtilisation du runtime Edge au lieu de Node.jsAjoutez export const runtime = 'nodejs'; au fichier de route.
Le dispatch GitHub échoue avec 404owner/repo incorrect ou scopes du token manquantsVérifiez GITHUB_REPO et accordez le scope repo au PAT.
Les temps de build sont longsnode_modules non mis en cache dans le CIAjoutez une étape de cache : actions/cache@v4 pour ~/.npm ou node_modules.
A server room with rows of black and white cabinets under blue cables
Photo de Taylor Vick sur Unsplash

10. Étendre le pipeline

  • Versionnage du contenu – Si vous utilisez le plugin Revisionary, vous pouvez également écouter les événements entry.unpublish et entry.delete pour nettoyer les pages obsolètes.
  • Builds de prévisualisation – Combinez le webhook avec les déploiements preview de Vercel pour générer une URL de brouillon pour les rédacteurs.
  • Feature flags – Transmettez un flag preview dans le payload et laissez la route Next.js décider de revalider le site en production ou un déploiement de prévisualisation.

En connectant les webhooks de Strapi 5 à un endpoint ISR on‑demand protégé dans Next.js et à un workflow GitHub Actions, vous obtenez une boucle CI/CD entièrement automatisée : chaque publication invalide instantanément le cache statique et déclenche un nouveau build. L’approche respecte les bonnes pratiques de sécurité, fonctionne avec TypeScript, et peut être étendue avec le journal d’audit de Revisionary pour une traçabilité complète.

Lectures complémentaires

Mots-clésCI/CDGitHub ActionsStrapiNext.jsTypeScriptWebhooks

Sources

  1. Strapi webhook documentation
  2. Next.js Incremental Static Regeneration guide
  3. Next.js Edge runtime limitations

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