Un client m’a demandé, il y a quelques semaines, pourquoi son menu de navigation n’apparaissait nulle part dans les réponses de l’API REST de son site WordPress. La réponse est simple : contrairement aux articles, aux pages ou aux catégories, les menus de navigation ne font partie d’aucune route native. WordPress 5.4 gère les menus comme un objet interne à l’administration, pensé pour wp_nav_menu() et le thème, pas pour une consommation externe.
Sur un projet headless, ce trou est bloquant dès la page d’accueil. Un front découplé a besoin de connaître la structure du menu principal, ses libellés, ses liens et sa hiérarchie éventuelle (sous-menus). Deux approches s’offrent à vous : passer par une extension existante, ou écrire votre propre route REST. Je détaille les deux, avec leurs compromis respectifs.
Pourquoi les menus ne sont pas exposés nativement
Techniquement, un menu WordPress repose sur une taxonomie interne (nav_menu) et un type de contenu (nav_menu_item), tous deux marqués public à false et show_in_rest à false dans le cœur. Ce choix n’est pas un oubli : les éléments de menu contiennent des méta-données complexes (profondeur, parent, type de cible — page, article, lien personnalisé, catégorie) que l’équipe cœur n’a jamais jugé prioritaire de normaliser pour une API publique.
Résultat concret : interroger /wp-json/wp/v2/menu-items renvoie une erreur 404 sur une installation par défaut. Il faut soit modifier la visibilité de ces objets, soit construire une route qui les traduit en JSON exploitable.
Solution 1 : l’extension WP REST API Menus
La solution la plus rapide consiste à installer une extension dédiée, comme WP REST API Menus. Une fois activée, elle ajoute une route du type /wp-json/menus/v1/menus/{slug} qui renvoie l’arborescence complète du menu demandé, avec pour chaque élément son titre, son URL, son identifiant de parent et ses enfants imbriqués.

Cette approche convient très bien pour un site vitrine ou un blog dont le menu ne change pas souvent. Elle a toutefois deux limites que j’ai rencontrées en production : la structure de réponse dépend entièrement de l’extension (donc de sa maintenance), et il faut connaître à l’avance le slug du menu enregistré via register_nav_menu().
Exemple de réponse
{
"ID": 12,
"items": [
{
"title": "Accueil",
"url": "https://exemple.fr/",
"object_id": "2",
"object": "page",
"child_items": []
},
{
"title": "Blog",
"url": "https://exemple.fr/blog/",
"object_id": "8",
"object": "page",
"child_items": []
}
]
}
Solution 2 : une route REST personnalisée avec register_rest_route
Sur les projets où je maîtrise le thème, je préfère écrire ma propre route plutôt que d’ajouter une dépendance externe. La fonction wp_get_nav_menu_items() retourne les éléments bruts d’un menu à partir de son identifiant ou de son emplacement enregistré ; il suffit de les reformater.