vendredi 25 septembre 2026

À propos

Contact

Headless & API

Nuxt Content et WordPress headless : synchroniser deux sources dans un site

Pages institutionnelles en Markdown via Nuxt Content, articles dynamiques depuis WordPress : retour sur un site hybride et ses pièges de routage.

Par Clément Hadrot • 14 janvier 2024 • 5 min de lecture • Aucun commentaire
Nuxt Content et WordPress headless : synchroniser deux sources dans un site

Le brief de ce projet, une association professionnelle du secteur médical, tenait en une phrase qui a fait grincer des dents en interne : « nos pages institutionnelles ne changent jamais, mais nos actualités doivent pouvoir être publiées par notre chargée de communication sans toucher au code ». Deux besoins, deux publics, deux rythmes de mise à jour radicalement différents.

La réponse technique retenue a été de séparer les deux flux à la source plutôt que de forcer une seule brique à tout gérer. Les pages institutionnelles (mentions légales, présentation, gouvernance) ont été écrites en Markdown et gérées par Nuxt Content directement dans le dépôt Git du site. Les actualités, elles, restent dans un WordPress headless consommé via l’API REST, pour que la chargée de communication garde son interface familière.

Le découpage du routage

Nuxt Content construit ses routes à partir de l’arborescence des fichiers Markdown présents dans le dossier content/. Une page à l’adresse content/gouvernance.md devient automatiquement accessible sur /gouvernance. De son côté, un article WordPress doit être servi par une route dynamique Nuxt classique, du type pages/actualites/[slug].vue, qui va chercher son contenu à l’exécution via l’API REST.

Le piège immédiat : rien n’empêche, sur le papier, qu’un fichier Markdown et un article WordPress portent le même slug. Si une chargée de communication publie un article intitulé « Gouvernance » avec le slug gouvernance, WordPress générera une route en conflit direct avec la page institutionnelle Nuxt Content existante. Sans garde-fou, le comportement dépend alors de l’ordre de résolution des routes dans Nuxt, ce qui est fragile et imprévisible pour l’équipe éditoriale.

La solution : un préfixe réservé et non négociable

La règle adoptée a été simple et documentée dans le manuel remis au client : tout contenu géré par WordPress est systématiquement préfixé par /actualites/ dans l’URL, et ce préfixe est explicitement interdit côté Nuxt Content via une vérification au build.

L'essentiel à retenir : Deux sources de vérité imposent une réservation stricte des préfixes d'URL ; Nuxt Content gère les pages statiques, WordPress les contenus qui changent souvent ; Le moteur de recherche interne doit interroger les deux sources séparément
// nuxt.config.ts (extrait)
export default defineNuxtConfig({
  hooks: {
    'content:file:beforeParse': (file) => {
      if (file._path?.startsWith('/actualites')) {
        throw new Error(
          `Le préfixe /actualites est réservé à WordPress : ${file._path}`
        );
      }
    },
  },
});

Ce garde-fou transforme une erreur de routage silencieuse en erreur de build explicite, détectée avant la mise en ligne plutôt que découverte par un visiteur qui tombe sur la mauvaise page.

Le cas particulier de la page d’accueil

La page d’accueil devait afficher à la fois un bloc institutionnel figé (Markdown) et les trois dernières actualités (WordPress). Elle a donc été traitée comme une troisième catégorie : une page Nuxt Content classique, dont le composant Vue effectue en plus un appel côté client vers l’API REST WordPress pour récupérer les articles récents, avec un état de chargement dédié pendant l’hydratation.

Synchroniser la recherche interne sur les deux sources

Le moteur de recherche du site posait un problème similaire : une recherche sur « statuts » devait pouvoir remonter aussi bien la page institutionnelle des statuts de l’association (Markdown) qu’un article d’actualité qui en parlerait (WordPress). Interroger une seule source aurait donné des résultats incomplets.

La solution retenue interroge les deux sources en parallèle et fusionne les résultats côté serveur, avec une pondération légèrement supérieure pour les pages institutionnelles, jugées plus stables et donc plus fiables comme réponse à une recherche :

const [pagesStatiques, articlesWp] = await Promise.all([
  queryContent('/').where({ _path: { $not: /^\/actualites/ } })
    .find(),
  $fetch('https://cms.exemple.org/wp-json/wp/v2/search', {
    params: { search: q, per_page: 10 },
  }),
]);

Les limites rencontrées

  • La prévisualisation d’un brouillon WordPress ne bénéficie pas du rendu statique de Nuxt Content : elle passe par une route dédiée en rendu côté serveur, ce qui a demandé une configuration séparée dans nuxt.config.ts.
  • Le fil d’Ariane, généré dynamiquement, doit savoir distinguer une route Markdown d’une route WordPress pour construire le bon chemin, ce qui a nécessité une petite fonction utilitaire partagée entre les deux systèmes.
  • Toute modification du préfixe réservé (par exemple si un jour les actualités devaient migrer vers /actus) impose de mettre à jour deux configurations distinctes, une source d’oubli potentiel documentée dans le README technique du projet.

Deux sources de contenu dans un même site ne posent jamais de problème techniquement insurmontable ; elles posent un problème de gouvernance des URL qu’il faut trancher avant d’écrire la première ligne de routage.

Notre verdict

Un an après la mise en ligne, ce découpage a tenu sans incident de routage. La clé n’a pas été un outil particulier, mais la décision précoce de réserver un préfixe d’URL de façon stricte et vérifiée au build. Un projet qui mélange plusieurs sources de contenu gagne à traiter cette réservation comme une règle d’architecture non négociable, documentée et testée automatiquement, plutôt que comme une convention informelle vouée à être oubliée par le prochain développeur qui touchera au projet.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi