Document Service de Strapi 5 : brouillons, locales et requêtes
Découvrez comment le Document Service de Strapi 5 remplace l'Entity Service, prend en charge documentId et gère brouillons, publication et contenus traduits.
Par l’équipe Revisionary · · 7 min de lecture
L'API Document Service de Strapi 5 remplace l'ancien Entity Service pour offrir une approche unifiée et centrée sur les documents lors de l'interrogation et de la manipulation du contenu. Au lieu de gérer des lignes distinctes en base de données pour les versions brouillon, publiée et traduite d'un enregistrement, vous interagissez désormais avec un unique documentId qui encapsule tous les états et toutes les locales d'une entrée. Comprendre comment requêter, modifier, publier et filtrer via strapi.documents est indispensable pour développer des controllers personnalisés, des services et des lifecycles back-end dans Strapi 5.
Pourquoi Strapi 5 a remplacé l'Entity Service
Dans Strapi 4, les entrées de contenu reposaient strictement sur des identifiants numériques auto-incrémentés en base de données. Lorsque la fonctionnalité Draft & Publish était activée, Strapi stockait l'état brouillon et l'état publié sous forme de lignes distinctes au sein de la même table, en les reliant en coulisses. L'ajout du plugin d'internationalisation (i18n) complexifiait encore ce modèle : chaque locale générait une nouvelle ligne avec son propre ID, reliée par des tables de jointure relationnelles.
Cette architecture posait des problèmes récurrents aux développeurs :
- Requêter un enregistrement par son ID pouvait renvoyer un brouillon ou une entrée publiée selon des flags internes.
- La gestion des relations entre plusieurs locales entraînait souvent des liens rompus ou des mises à jour en cascade inattendues.
- Les intégrations tierces et les applications front-end devaient suivre plusieurs ID pour ce que les éditeurs considéraient comme un seul et même contenu.
Strapi 5 résout ce problème grâce au Document Service. Un document représente un contenu unique à travers l'ensemble de ses variantes. Chaque document se voit attribuer un identifiant textuel persistant et unique appelé documentId. L'id numérique existe toujours au niveau de la ligne de base de données pour les requêtes SQL brutes, mais le code de votre application, vos controllers personnalisés et vos API de contenu manipulent désormais le documentId.
Le concept fondamental : documentId vs id
Pour travailler efficacement avec Strapi 5, vous devez distinguer les lignes de la base de données des documents de contenu.
| Concept | Type d'identifiant | Portée | Utilisation |
|---|---|---|---|
| Document | documentId (string) | Universel à travers les brouillons, versions publiées et locales | Logique métier, requêtes Document Service, API REST/GraphQL |
| Ligne en base | id (integer) | Spécifique à un enregistrement exact en base de données (un seul état dans une seule locale) | Requêtes Knex de bas niveau, migrations de base de données héritées |
Lorsque vous écrivez de la logique métier, utilisez systématiquement documentId. Si vous mettez à jour un document via son documentId, Strapi oriente automatiquement la modification vers le bon enregistrement (brouillon ou publié) en fonction des paramètres fournis.
Accéder au Document Service
Vous accédez au Document Service à l'aide du helper global strapi.documents('api::contentType.contentType'). Cette méthode accepte l'UID du Content Type et retourne un client d'API adapté à ce schéma.
// Exemple : Récupération du Document Service pour une collection Article
const articleService = strapi.documents('api::article.article');Le Document Service prend en charge les opérations CRUD standard ainsi que des actions spécifiques aux documents :
findMany(): Rechercher plusieurs documents correspondant à des critères précis.findOne(): Récupérer un document unique via sondocumentId.findFirst(): Récupérer le premier document correspondant à un ensemble de filtres.create(): Créer un nouveau document à l'état de brouillon ou directement publié.update(): Modifier un document existant par sondocumentId.delete(): Supprimer un document complet, une locale spécifique ou un état particulier.publish(): Passer une version brouillon au statut publié.unpublish(): Supprimer la version publiée tout en conservant le brouillon intact.discardDraft(): Rétablir le brouillon avec le contenu de la version actuellement publiée.count(): Compter les documents correspondant aux filtres.
Requêter du contenu : findMany et findOne
Par défaut, les requêtes exécutées avec le Document Service ciblent l'état brouillon (draft), sauf si vous demandez explicitement les entrées publiées. Ce comportement diffère de l'API de contenu publique (REST et GraphQL), qui renvoie par défaut les données publiées aux requêtes non authentifiées.
Requêtes de base et filtrage
Lors de la récupération de documents, vous pouvez filtrer les champs, trier les enregistrements, charger les relations (populate) et sélectionner des attributs spécifiques :
// controllers/article.ts
export default {
async findFeatured(ctx) {
const articles = await strapi.documents('api::article.article').findMany({
fields: ['title', 'slug', 'publishedAt'],
filters: {
isFeatured: { $eq: true },
},
populate: {
author: {
fields: ['name', 'email'],
},
coverImage: true,
},
sort: { createdAt: 'desc' },
limit: 10,
});
ctx.body = articles;
},
};Requêter un document par documentId
Pour récupérer une entrée spécifique, passez directement documentId à findOne() :
const article = await strapi.documents('api::article.article').findOne({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
populate: ['category'],
});Si vous omettez le paramètre status, Strapi renvoie la version draft du document si elle existe, ou la version published si aucun brouillon n'est présent.
Gérer les états Draft & Publish
Draft & Publish est une fonctionnalité native dans Strapi 5, gérée directement au moyen de paramètres de requête plutôt que par des endpoints distincts ou des flags complexes en base de données.
Définir le paramètre status
L'option status accepte deux valeurs :
'draft': Cible les versions brouillons. Si une entrée comporte des modifications non publiées, elle retourne ces mises à jour en attente.'published': Cible exclusivement la version publiée et active. Si une entrée n'a jamais été publiée, une requête demandant son état publié retourneranull.
// Récupère uniquement la version publiée pour une page publique
const publishedArticle = await strapi.documents('api::article.article').findOne({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
status: 'published',
});
// Récupère la version brouillon pour un endpoint de prévisualisation
const draftArticle = await strapi.documents('api::article.article').findOne({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
status: 'draft',
});Publier, dépublier et abandonner les modifications
Strapi 5 propose des méthodes dédiées pour faire transiter les documents à travers les étapes du workflow, sans devoir mettre à jour manuellement des champs d'horodatage comme publishedAt.
const docService = strapi.documents('api::article.article');
const targetId = 'a1b2c3d4e5f6g7h8i9j0';
// 1. Publier un brouillon
const published = await docService.publish({
documentId: targetId,
});
// 2. Dépublier (repasse le contenu en brouillon uniquement, retire l'état publié)
const unpublished = await docService.unpublish({
documentId: targetId,
});
// 3. Abandonner le brouillon (annule les modifications non publiées et restaure l'état publié dans le brouillon)
const reverted = await docService.discardDraft({
documentId: targetId,
});Lorsque discardDraft() est appelée, toutes les modifications effectuées depuis la dernière publication sont effacées, réalignant ainsi le contenu du brouillon sur le document actuellement en ligne.
Travailler avec l'internationalisation (i18n)
Dans Strapi 5, les locales sont des sous-enregistrements d'un même document. Un unique documentId regroupe toutes les traductions d'une entrée. Il n'est donc plus nécessaire d'interroger des tables de jointure de traduction ni d'associer manuellement différentes entrées.
Requêter des locales spécifiques
Pour obtenir un document dans une locale précise, transmettez le paramètre locale :
// Récupère la traduction française d'un article
const frenchArticle = await strapi.documents('api::article.article').findOne({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
locale: 'fr',
status: 'published',
});Si vous souhaitez récupérer toutes les locales disponibles pour un ensemble de documents, utilisez locale: '*' dans findMany() :
const allTranslations = await strapi.documents('api::article.article').findMany({
locale: '*',
filters: {
category: { slug: 'technology' },
},
});Créer et mettre à jour du contenu localisé
Lors de la création d'une version traduite d'un document existant, vous ne faites pas appel à un service i18n séparé. Il vous suffit de fournir le documentId existant ainsi que le code de la nouvelle locale via update() ou create() :
// Ajout d'une version allemande à un document existant
const germanEntry = await strapi.documents('api::article.article').create({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
locale: 'de',
data: {
title: 'Einführung in Strapi 5',
content: 'Hier ist der Inhalt auf Deutsch...',
},
});Si vous appelez update() en spécifiant une locale, Strapi ne modifie que les données de cette langue, sans toucher aux autres :
await strapi.documents('api::article.article').update({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
locale: 'es',
data: {
title: 'Título Actualizado',
},
});Créer et mettre à jour du contenu
Lorsque vous créez du nouveau contenu avec le Document Service, vous pouvez décider si le document doit débuter comme un brouillon ou être publié immédiatement.
Créer des brouillons vs documents publiés
Par défaut, un appel à create() sans paramètre de workflow génère un enregistrement en brouillon :
// Crée un document en brouillon
const newDraft = await strapi.documents('api::article.article').create({
data: {
title: 'Upcoming Product Launch',
slug: 'upcoming-product-launch',
body: 'Draft content undergoing review.',
},
});Pour créer un document et le publier directement en un seul appel, définissez status: 'published' dans les paramètres :
// Crée et publie immédiatement le document
const liveArticle = await strapi.documents('api::article.article').create({
data: {
title: 'Breaking News Announcement',
slug: 'breaking-news',
body: 'This content is live immediately.',
},
status: 'published',
});Mettre à jour des champs spécifiques
Pour mettre à jour un document, vous ne transmettez que les champs que vous souhaitez modifier. Les champs relationnels acceptent des valeurs documentId ou des tableaux d'identifiants selon que la relation est simple ou polymorphique :
await strapi.documents('api::article.article').update({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
data: {
title: 'Revised Headline',
category: 'cat_987654321',
},
});Middlewares du Document Service
Strapi 5 introduit les Document Service Middlewares. Ceux-ci interceptent les appels au Document Service, remplaçant avantageusement de nombreux cas d'usage où les développeurs recouraient auparavant à des lifecycle hooks fragiles (beforeCreate, afterUpdate).
Les middlewares enveloppent les opérations dans une chaîne prévisible, ce qui permet de valider les données, de modifier les paramètres avant qu'ils n'atteignent la base de données ou de déclencher des effets de bord une fois les mutations effectuées.
Déclarer un middleware de document
Vous enregistrez les middlewares du Document Service au sein des fonctions register ou bootstrap de votre application dans src/index.ts :
// src/index.ts
import type { Core } from '@strapi/strapi';
export default {
register({ strapi }: { strapi: Core.Strapi }) {
strapi.documents.use(async (context, next) => {
// Vérifie la collection ciblée et l'action
if (context.uid === 'api::article.article' && context.action === 'create') {
// Inspecte ou modifie les paramètres entrants
if (context.params.data && !context.params.data.slug) {
context.params.data.slug = context.params.data.title
.toLowerCase()
.replace(/\s+/g, '-');
}
}
// Exécute le middleware suivant ou l'opération en base de données
const result = await next();
// Logique post-opération
if (context.uid === 'api::article.article' && context.action === 'publish') {
strapi.log.info(`Document published: ${result.documentId}`);
}
return result;
});
},
};À la différence des lifecycles classiques, les middlewares reçoivent l'intégralité du contexte de la requête du Document Service, y compris les options fields, populate, la locale et le status de publication.
Suppressions sécurisées : document vs locale
La suppression de contenu dans Strapi 5 offre un contrôle granulaire : vous pouvez choisir de purger une traduction précise ou d'éliminer l'entité entière.
Supprimer une locale spécifique
Si vous transmettez à la fois un documentId et une locale, seule cette traduction sera supprimée. Les autres locales associées au même document restent intactes :
// Supprime uniquement la traduction espagnole
await strapi.documents('api::article.article').delete({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
locale: 'es',
});Supprimer le document complet
Si vous omettez le paramètre locale ou si vous supprimez la locale principale, le document est effacé pour l'ensemble des locales et de ses états :
// Supprime l'ensemble des locales, brouillons et versions publiées de ce document
await strapi.documents('api::article.article').delete({
documentId: 'a1b2c3d4e5f6g7h8i9j0',
});Lors de la gestion de workflows éditoriaux complexes impliquant plusieurs contributeurs sur différentes locales, la traçabilité des suppressions et des révisions devient souvent indispensable. Si votre équipe a besoin de capacités complètes de restauration, des plugins comme Revisionary conservent chaque version de vos contenus Strapi. Ils permettent de visualiser les différences au niveau des champs, d'effectuer des restaurations ciblées et d'accéder à une corbeille pour récupérer les états de documents supprimés, locale par locale.
Migrer depuis l'Entity Service de Strapi 4
Lors de la mise à niveau de votre code back-end de Strapi 4 vers Strapi 5, inspectez votre base de code pour repérer les appels à strapi.entityService et les transformer en strapi.documents.
Principales différences de migration
- Appels de méthodes : Remplacez
strapi.entityService.findMany(uid, params)parstrapi.documents(uid).findMany(params). - Références d'identifiants : Remplacez les recherches par ID numérique (
findOne(uid, id)) par des document IDs (findOne({ documentId })). - Logique de publication : Supprimez les mises à jour manuelles de type
publishedAt: new Date(). Utilisez à la placestrapi.documents(uid).publish({ documentId }). - Syntaxe de populate : Les structures de populate dans Strapi 5 suivent une syntaxe objet similaire, mais les relations retournent dorénavant le
documentIdaux côtés de l'enregistrement. - Valeur par défaut pour les brouillons : Dans Strapi 4, les requêtes de l'Entity Service contournaient Draft & Publish à moins d'avoir configuré des paramètres d'état de publication explicites. Dans Strapi 5,
strapi.documentscible par défautstatus: 'draft'. Si vous avez besoin des données en ligne, spécifiez expressémentstatus: 'published'.
Voici une comparaison directe pour une requête courante :
// --- Strapi 4 (Entity Service) ---
const strapi4Result = await strapi.entityService.findOne('api::article.article', 42, {
populate: { author: true },
publicationState: 'live',
});
// --- Strapi 5 (Document Service) ---
const strapi5Result = await strapi.documents('api::article.article').findOne({
documentId: 'c1d2e3f4g5h6i7j8k9l0',
status: 'published',
populate: { author: true },
});Conclusion
L'API Document Service de Strapi 5 unifie les brouillons, les publications et les traductions sous un unique documentId cohérent. En adaptant vos controllers personnalisés, vos tâches cron et vos services vers strapi.documents(), vous obtenez un comportement de requête prévisible, des structures de données plus saines et une prise en charge native des workflows de localisation complexes. Veillez à ce que vos requêtes définissent explicitement les paramètres status et locale souhaités afin de récupérer très exactement la version du contenu attendue par votre application.
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.