Comment annuler le contenu dans Strapi 5 pour un frontend Next.js 15
Apprenez à activer l’historique du contenu dans Strapi 5, à récupérer les versions via GraphQL ou REST, et à implémenter un rollback fiable dans une appli Next.js 15, CI/CD.
Par l’équipe Revisionary · · 5 min de lecture
Strapi 5 peut stocker chaque modification que vous effectuez sur des types de contenu versionnés, et Next.js 15 peut re‑valider les pages statiques lorsqu’un brouillon est publié. En combinant l’Historique du contenu de Strapi (fourni par le plugin Revisionary) avec l’API Document Service, GraphQL ou l’API REST, vous pouvez bâtir un mécanisme de rollback fiable qui fonctionne depuis l’interface d’administration jusqu’au code front‑end.
1. Prérequis et fonctionnement de la fonctionnalité
- Strapi 5 installé sur Node 18‑24 avec une base de données prise en charge (PostgreSQL, MySQL ou SQLite).
- Le plugin Revisionary actif (plan Growth ou Enterprise) – il enregistre les différences au niveau des champs, propose une chronologie des versions et conserve une corbeille pour les entrées supprimées.
- Un projet Next.js 15 écrit en React et TypeScript.
- Connaissance de base du workflow Draft & Publish de Strapi.
Lorsque ces conditions sont réunies, chaque modification effectuée via le panneau d’administration crée une nouvelle version qui peut être listée, comparée et restaurée. Le front‑end peut interroger la version publiée actuelle, un brouillon spécifique ou n’importe quel instantané historique, rendant le rollback possible en quelques appels d’API.
2. Activer Draft & Publish et l’historique du contenu
- Activer Draft & Publish pour les types de contenu que vous souhaitez versionner (par ex.
article). Dans le Content‑Type Builder de Strapi, activez Draft & Publish. - Installer Revisionary depuis le Marketplace, puis lancez l’installeur comme décrit dans le guide /docs/getting-started/install. Le plugin ajoute un panneau Version history à droite du Content Manager.
- Configurer la rétention si vous avez besoin d’une fenêtre plus courte que la valeur par défaut (Growth = 14 jours, Enterprise = 30 jours). Ajoutez dans
/config/admin.js:
module.exports = ({ env }) => ({
history: { retentionDays: 7 }, // conserver les versions pendant une semaine
});Ce paramètre ne peut pas être étendu au‑delà des limites du plan, mais le réduire aide à maîtriser l’espace de stockage.
3. Lister les versions – les points d’accès API
3.1 Point d’accès Content‑Manager (admin uniquement)
L’UI admin appelle un point d’accès interne qui peut aussi être utilisé depuis un script côté serveur :
GET /content-manager/history-versions?contentType=api::article.article&documentId=abc123Il renvoie un tableau paginé d’objets version contenant id, createdAt, author et un instantané data des champs à ce moment‑là.
3.2 Document Service API – requêtes conscientes des versions
Le Document Service de Strapi vous permet de récupérer une version précise en passant le paramètre status :
// Récupérer la version publiée
const published = await strapi.documents('api::article.article').findOne({
documentId: 'abc123',
status: 'published',
});
// Récupérer le brouillon (les dernières modifications non publiées)
const draft = await strapi.documents('api::article.article').findOne({
documentId: 'abc123',
status: 'draft',
});L’énumération status est documentée sur la page /docs/reference/content-api.
3.3 GraphQL – status & publicationFilter
Lorsque vous utilisez le plugin GraphQL, vous pouvez demander une version précise directement dans la requête :
query GetArticle($id: ID!, $status: PublicationStatus!) {
article(id: $id, status: $status) {
title
content
updatedAt
}
}Les valeurs supportées de PublicationStatus sont DRAFT et PUBLISHED. Vous pouvez également filtrer selon la relation entre brouillon et version publiée avec publicationFilter, par ex. MODIFIED pour obtenir les brouillons contenant des changements non publiés.
3.4 REST – gestion explicite des brouillons
Pour les routes REST classiques, ajoutez ?status=draft pour garder le document en mode brouillon ; omettre le paramètre publie immédiatement.
PUT /api/articles/123?status=draft # conserve comme brouillon
PUT /api/articles/123 # publie instantanémentVoir l’extrait officiel sur /docs/reference/content-api.
4. Implémenter le rollback dans un front‑end Next.js 15
4.1 Vue d’ensemble de l’architecture
- Couche API – une route API Next.js fine (
/pages/api/rollback.ts) qui communique avec Strapi via GraphQL ou les points d’accès REST du Document Service. - Côté client – un composant React dans le tableau de bord admin qui liste les versions disponibles et déclenche l’API de rollback.
- Revalidation – après un rollback réussi, la route appelle
res.revalidate('/articles/[slug]')afin que les pages ISR soient régénérées.
4.2 Helper TypeScript pour récupérer les versions
Créez un module utilitaire lib/strapi.ts :
import fetch from 'node-fetch';
const STRAPI_URL = process.env.STRAPI_URL!;
const TOKEN = process.env.STRAPI_TOKEN!;
export async function fetchVersions(documentId: string) {
const res = await fetch(
`${STRAPI_URL}/content-manager/history-versions?contentType=api::article.article&documentId=${documentId}`,
{
headers: { Authorization: `Bearer ${TOKEN}` },
}
);
if (!res.ok) throw new Error('Failed to load versions');
return res.json(); // { data: Version[], meta: {...} }
}
export async function restoreVersion(documentId: string, versionId: string) {
const res = await fetch(
`${STRAPI_URL}/content-manager/restore-version`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${TOKEN}`,
},
body: JSON.stringify({ documentId, versionId }),
}
);
if (!res.ok) throw new Error('Rollback failed');
return res.json();
}Le point d’accès restore-version fait partie de l’API interne de Revisionary ; il copie l’instantané sélectionné dans le brouillon actuel et marque l’entrée comme Modified.
4.3 Route API qui déclenche le rollback et la revalidation
Fichier : pages/api/articles/[id]/rollback.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { restoreVersion } from '@/lib/strapi';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') return res.status(405).end();
const { id } = req.query as { id: string };
const { versionId } = req.body as { versionId: string };
try {
await restoreVersion(id, versionId);
// Re‑valider la page ISR affichant l’article
await res.revalidate(`/articles/${id}`);
return res.status(200).json({ message: 'Rollback successful and page revalidated' });
} catch (e) {
console.error(e);
return res.status(500).json({ error: 'Rollback failed' });
}
}res.revalidate de Next.js 15 fonctionne sur Vercel, Netlify Edge ou toute plateforme supportant la revalidation à la demande.
4.4 Composant UI côté front‑end
import { useEffect, useState } from 'react';
import { fetchVersions, restoreVersion } from '@/lib/strapi';
interface Version { id: string; createdAt: string; data: Record<string, any>; }
export default function VersionRollback({ articleId }: { articleId: string }) {
const [versions, setVersions] = useState<Version[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
fetchVersions(articleId).then((res) => setVersions(res.data));
}, [articleId]);
const handleRollback = async (versionId: string) => {
setLoading(true);
await fetch('/api/articles/' + articleId + '/rollback', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ versionId }),
});
setLoading(false);
// Rafraîchir éventuellement l’UI après le rollback
window.location.reload();
};
return (
<section>
<h2>Historique des versions</h2>
{loading && <p>Rollback en cours…</p>}
<ul>
{versions.map((v) => (
<li key={v.id}>
{new Date(v.createdAt).toLocaleString()} –
<button onClick={() => handleRollback(v.id)} disabled={loading}>
Revenir à cette version
</button>
</li>
))}
</ul>
</section>
);
}Le composant récupère la liste des versions via le helper, affiche les horodatages et appelle la route API pour effectuer la restauration.
5. Rollback uniquement GraphQL (optionnel)
Si votre équipe préfère un flux purement GraphQL, vous pouvez exposer une mutation personnalisée qui appelle en interne le service Revisionary. Ajoutez dans ./src/api/article/services/article.js :
module.exports = {
async rollback(ctx) {
const { documentId, versionId } = ctx.request.body;
await strapi.service('plugin::revisionary.version').restore({ documentId, versionId });
return { ok: true };
},
};Puis enregistrez la mutation dans ./src/api/article/config/schema.graphql :
extend type Mutation {
rollbackArticle(documentId: ID!, versionId: ID!): RollbackResponse!
}
type RollbackResponse { ok: Boolean! }Les clients peuvent désormais exécuter :
mutation Rollback($doc: ID!, $ver: ID!) {
rollbackArticle(documentId: $doc, versionId: $ver) {
ok
}
}Après le succès de la mutation, déclenchez une requête de revalidation depuis le front‑end (par ex. fetch('/api/revalidate?slug=article-slug')).
6. Tester le flux de rollback
6.1 Tests unitaires de la route API
import handler from '@/pages/api/articles/[id]/rollback.ts';
import { createMocks } from 'node-mocks-http';
test('POST déclenche restore et revalidation', async () => {
const { req, res } = createMocks({
method: 'POST',
url: '/api/articles/123/rollback',
body: { versionId: 'v456' },
});
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
const data = JSON.parse(res._getData());
expect(data.message).toContain('successful');
});Le test vérifie que la route renvoie 200 et que la forme du JSON correspond aux attentes. Vous pouvez moquer restoreVersion et res.revalidate avec des espions Jest.
6.2 Test d’intégration avec le Document Service
import { Strapi } from '@strapi/strapi';
test('restore crée un nouveau brouillon identique à la version choisie', async () => {
const strapi = await Strapi().load();
const articleId = 'abc123';
const versions = await strapi.documents('api::article.article').findMany({
documentId: articleId,
populate: '*',
status: 'draft',
});
const target = versions[0];
// Simuler un rollback
await strapi.service('plugin::revisionary.version').restore({ documentId: articleId, versionId: target.id });
const after = await strapi.documents('api::article.article').findOne({ documentId: articleId, status: 'draft' });
expect(after.title).toBe(target.data.title);
});Le test d’intégration s’exécute contre une instance Strapi temporaire (démarrée en CI) et valide que le brouillon restauré correspond à l’instantané.
7. Considérations CI/CD
- Exécuter les tests end‑to‑end dans GitHub Actions avant le déploiement. Exemple :
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: strapi
POSTGRES_PASSWORD: strapi
POSTGRES_DB: strapi
ports: ['5432:5432']
steps:
- uses: actions/checkout@v4
- name: Install deps
run: yarn install --frozen-lockfile
- name: Build Strapi
run: yarn build:api
- name: Start Strapi
run: yarn start &
env:
DATABASE_CLIENT: postgres
DATABASE_HOST: localhost
DATABASE_PORT: 5432
DATABASE_NAME: strapi
DATABASE_USERNAME: strapi
DATABASE_PASSWORD: strapi
- name: Run tests
run: yarn test- Déployer Next.js sur Vercel (ou toute plateforme) et ajouter un webhook dans Strapi qui appelle le point d’accès
/api/revalidatede Vercel après une publication ou un rollback réussi. La charge utile du webhook peut être filtrée pour ne s’activer que sur le type de contenuarticle. - Cache sensible aux versions – définissez
Cache-Control: stale-while-revalidatesur les requêtes GraphQL afin que les navigateurs conservent la page publiée précédente pendant la régénération.
8. Observabilité et journal d’audit
Lorsque le plugin audit‑logs (Enterprise) est activé, Strapi enregistre chaque restauration de version, chaque publication et chaque suppression. Vous pouvez transmettre ces logs vers un système externe (ELK, Datadog) via un middleware personnalisé :
module.exports = () => async (ctx, next) => {
await next();
const { method, url, state } = ctx;
if (method === 'POST' && url.includes('/restore-version')) {
console.log(`[audit] ${state.user?.email} restored version ${ctx.request.body.versionId}`);
}
};Ces traces vous aident à répondre aux exigences de conformité et à repérer rapidement les rollbacks inattendus.
9. Récapitulatif
- Activer Draft & Publish et installer Revisionary.
- Configurer la rétention selon votre politique de conservation des données.
- Utiliser l’API Document Service (ou GraphQL) pour récupérer la version publiée actuelle et les brouillons nécessaires.
- Construire une route API Next.js qui appelle le point d’accès
restore-versionde Revisionary, puis déclencher la revalidation ISR. - Ajouter un composant React dans votre UI éditoriale pour lister les versions et lancer la requête de rollback.
- Rédiger tests unitaires et d’intégration qui mockent le client Strapi et vérifient l’état du brouillon après restauration.
- Intégrer le flux dans votre pipeline CI/CD et configurer la surveillance via les audit‑logs pour une visibilité en production.
En suivant ces étapes, vous obtenez un mécanisme de rollback prêt pour la production, compatible avec REST, GraphQL et le Document Service, tout en maintenant le front‑end synchronisé grâce à l’ISR de Next.js. Le même modèle peut être étendu à d’autres types de contenu : il suffit d’activer la version dans les réglages de Revisionary.
Sources
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.