Douze ans. C’est le temps qu’un cabinet de droit des affaires basé à Nancy a passé sur Joomla avant de nous appeler. Le site public affichait les associés, les domaines de compétence, les publications et un espace « actualités juridiques » alimenté par deux collaborateurs. Rien d’extraordinaire, sauf que la maintenance était devenue un calvaire : chaque mise à jour de composant cassait une extension tierce, et personne dans l’équipe ne savait plus quel plugin gérait quoi.
Le déclencheur n’était pourtant pas technique. C’est la direction qui a posé une exigence claire : le futur site ne devait jamais mélanger les contenus publics (biographies, articles, offres d’emploi) avec l’extranet sécurisé où les clients suivent leurs dossiers. Cette séparation stricte a orienté tout le choix d’architecture vers un headless.
Pourquoi Joomla ne suffisait plus
Joomla gère les droits par niveaux d’accès (Access Levels), un système efficace pour un site classique mais rigide dès qu’on veut exposer sélectivement certains contenus à une application externe. L’équipe avait bricolé un plugin maison qui dupliquait certains articles vers une table SQL séparée, lue ensuite par un script PHP indépendant. Ce pont artisanal fonctionnait, jusqu’au jour où une mise à jour de sécurité a changé la structure de la table sans prévenir personne.
Le vrai problème de fond, c’est que Joomla n’a pas été pensé pour découpler proprement la donnée de sa présentation. On peut forcer les choses avec des composants comme la Web Services API introduite dans Joomla 4, mais elle reste jeune et peu documentée comparée à l’API REST de WordPress, stabilisée depuis 2016 et couverte par une documentation exhaustive sur developer.wordpress.org.
La bascule vers WordPress en API REST
Nous avons recréé six types de contenus avec register_post_type() : associés, domaines d’intervention, publications, offres d’emploi, actualités et témoignages clients (ces derniers publiés uniquement après validation manuelle, jamais liés à un dossier réel). Chaque type utilise show_in_rest à true avec un rest_base explicite, de façon à obtenir des routes lisibles comme /wp-json/wpm/v1/associes.

Le point sensible restait la confidentialité. Un cabinet d’avocats manipule des informations couvertes par le secret professionnel, et il était hors de question qu’un champ personnalisé mal exposé laisse fuiter ne serait-ce qu’un nom de dossier. Nous avons donc construit la surface de l’API en partant du principe inverse de l’habitude : tout est fermé par défaut, et seuls les champs explicitement whitelistés sortent dans la réponse JSON.
add_action( 'rest_api_init', function () {
register_rest_field( 'associe', 'specialites', array(
'get_callback' => function ( $post ) {
$terms = get_the_terms( $post['id'], 'domaine' );
if ( ! $terms || is_wp_error( $terms ) ) {
return array();
}
return wp_list_pluck( $terms, 'name' );
},
'schema' => array(
'type' => 'array',
'items' => array( 'type' => 'string' ),
'description' => 'Domaines de compétence de l\'associé',
),
) );
} );
Ce que l’architecture ne couvre pas
Cet article ne traite volontairement pas de l’authentification : l’extranet sécurisé des clients repose sur un système entièrement distinct, hébergé sur un sous-domaine séparé, sans aucune passerelle technique avec l’API publique décrite ici. Mélanger les deux aurait été une erreur d’architecture autant qu’une faute professionnelle vis-à-vis du secret des affaires.
Le front, développé avec Next.js, consomme les six endpoints via des appels côté serveur au moment du build, avec une revalidation programmée toutes les heures. Aucune requête n’est faite depuis le navigateur du visiteur vers l’API WordPress : tout transite par le serveur Next.js, ce qui réduit encore la surface d’exposition.
Les ajustements imprévus
- Les biographies des associés contenaient des mises en forme Word collées telles quelles, à nettoyer avant import
- Le champ « publications » nécessitait un lien vers un PDF hébergé, géré via un champ URL simple plutôt qu’un média WordPress
- Les offres d’emploi devaient disparaître automatiquement de l’API après leur date de clôture, réglé avec une requête
WP_Queryfiltrée sur une meta de date
Sur un site à contenu sensible, la meilleure protection contre la fuite de données n’est pas un mur plus haut : c’est une API qui ne sait tout simplement pas que la donnée sensible existe.
Le bilan après six mois
Le temps de chargement perçu sur les pages publiques a nettement baissé, mais ce n’était pas l’objectif premier du cabinet. Ce qui comptait, c’était la clarté du périmètre : aujourd’hui, n’importe quel développeur qui rejoint l’équipe peut lire les six déclarations register_post_type() et comprendre en quelques minutes ce que l’API expose, sans avoir à fouiller dans des extensions Joomla accumulées sur douze ans.
Ce qu’il faut retenir
Le choix d’un headless ne s’est pas fait pour suivre une mode, mais parce que la contrainte de confidentialité imposait une frontière nette entre contenu public et données de dossiers. WordPress a gagné face à Joomla non pas sur la performance brute, mais sur la maturité de son API REST et la lisibilité du code qu’elle permet d’écrire. Pour un cabinet, où chaque ligne de code touchant à la donnée doit pouvoir être expliquée à un responsable de la conformité, cet argument a pesé plus lourd que n’importe quel benchmark.