Construire un plugin WordPress avec l’API REST : exposer les données avant l’interface
WordPress propose nativement une API REST qui permet d’exposer les données de manière structurée et accessible. Disponible via l’endpoint wp-json, elle a été conçue pour séparer clairement la récupération des données de leur affichage, et ouvrir WordPress à d’autres usages que le simple rendu de pages. Dans cet article, nous proposons de revenir sur ce rôle. Comprendre ce que permet réellement l’API REST de WordPress, comment fonctionne wp-json, et en quoi cette couche modifie la manière de concevoir un plugin. L’objectif n’est pas d’ajouter de la complexité, mais de clarifier les responsabilités entre serveur et interface.
Pour ancrer ces notions dans un cas concret, nous mettons en place un plugin chargé d’exposer la liste des articles de Puce & Média via une API REST, puis de les afficher côté navigateur sous forme de tableau HTML. Le shortcode reste présent, mais il se limite à un rôle d’ancrage. L’essentiel de la logique repose sur une API claire et réutilisable. Cette approche constitue volontairement une première étape. Elle permet de se concentrer sur la couche API, sans framework, avant d’envisager une suite où l’affichage et les interactions seront confiés à une couche dédiée, comme Vue.js, à partir des mêmes données exposées.
Scénario de mise en place et objectif du plugin
L’objectif de ce plugin est volontairement simple et concret. Il s’agit de mettre en pratique l’API REST de WordPress, en exposant des données structurées puis en les exploitant côté navigateur, sans framework. Le scénario retenu a pour finalité de proposer un tableau interactif des articles du blog (titre, date, catégories, mots clés…), construit dynamiquement à partir des données exposées par le plugin, et non généré côté serveur. Concrètement, le plugin se limite à trois responsabilités claires :
- exposer les données via un endpoint REST pensé pour l’affichage
- fournir un shortcode minimal, servant de point d’ancrage
- laisser le navigateur construire l’interface HTML à partir des données reçues

Ce cadre nous sert de fil conducteur tout au long de l’article. Il permet d’aborder l’API REST WordPress dans un contexte réel, en complément de l’article « Comprendre le concept de REST pour le développement d’applications web efficaces », et de poser une base réutilisable, indépendante de tout affichage ou technologie front‑end spécifique.
Pourquoi repenser l’accès aux données dans WordPress
Pendant longtemps, l’accès dynamique aux données dans WordPress s’est appuyé sur une mécanique devenue familière : admin-ajax.php. Cette approche a rendu de nombreux services en permettant de déclencher des traitements côté serveur et de renvoyer des fragments de données ou de HTML sans rechargement complet. Le schéma reste relativement simple. Une action JavaScript appelle admin-ajax.php, WordPress exécute une fonction PHP associée, puis renvoie une réponse, souvent déjà formatée pour l’affichage. Ce fonctionnement n’est ni faux ni obsolète par nature, et demeure pertinent pour des usages ciblés ou ponctuels.
Ses limites apparaissent toutefois dès que l’on cherche à structurer l’accès aux données dans la durée, à multiplier les points de consommation, ou à dissocier clairement le fond de la forme. Depuis l’arrivée de l’API REST native (version 4.7+), WordPress propose une autre logique. Les données ne sont plus renvoyées en réponse à une action isolée, mais exposées via des endpoints identifiés et réutilisables. Le serveur fournit une représentation des données, tandis que l’interface se charge de leur mise en forme.

Dans le scénario posé précédemment, ce changement est central. Le tableau des articles n’est plus alimenté par une action AJAX produisant du HTML, mais par une source de données stable, indépendante de l’affichage. Le navigateur devient responsable de la transformation et de la présentation.
Ces deux mécanismes coexistent et répondent à des logiques distinctes. admin-ajax.php s’inscrit dans une logique d’exécution ponctuelle, tandis que l’API REST expose des données de manière structurée et réutilisable. Cette juxtaposition permet de mieux répartir les rôles et de poser les bases de plugins plus lisibles, plus durables et ouverts aux évolutions futures.
Poser le socle du plugin
Avant d’exposer des données ou de construire une interface, il est essentiel de poser un socle clair. Un plugin WordPress bien structuré n’est pas nécessairement volumineux. Il repose avant tout sur une organisation lisible, des responsabilités bien identifiées et une initialisation maîtrisée.
Arborescence minimale
Dans notre scénario, le plugin reste volontairement simple. Une arborescence réduite suffit à condition qu’elle soit cohérente et explicite. Le fichier principal sert de point d’entrée. Les fichiers de logique REST sont isolés dans un dossier dédié, tandis que les scripts et styles destinés à l’affichage sont regroupés dans assets. Cette séparation prépare déjà la distinction entre données et interface.
pem-articles-table/
├─ pem-articles-table.php
├─ inc/
│ └─ rest-endpoints.php
├─ assets/
│ ├─ js/
│ │ └─ table.js
│ └─ css/
│ └─ table.css

Rôle du fichier principal pem-articles-table.php
Le fichier principal du plugin joue un rôle du chef d’orchestre. Il déclare le plugin, charge les dépendances nécessaires et enregistre les briques de base.
<?php
/**
* Plugin Name: Pem Articles Table
* Description: Expose les articles via l’API REST et prépare leur affichage en tableau.
*/
if (!defined('ABSPATH')) {
exit;
}
require_once __DIR__ . '/inc/rest-endpoints.php';Aucune logique métier n’est développée ici. Le fichier se contente de poser le cadre et de relier les différentes parties du plugin. Le shortcode reste volontairement minimal. Il ne génère pas le tableau, il prépare simplement le point d’ancrage nécessaire à l’affichage côté navigateur.
add_shortcode('pem_articles_table', function () {
return '<div id="pem-articles-table"></div>';
});
Cette approche permet de garder le contenu WordPress lisible et d’éviter d’enfermer la logique d’affichage dans le PHP. Les scripts et styles sont chargés uniquement lorsque le shortcode est présent. Cela évite d’alourdir inutilement l’ensemble du site.
add_action('wp_enqueue_scripts', function () {
if (!is_singular()) {
return;
}
wp_enqueue_script(
'pem-articles-table',
plugins_url('assets/js/table.js', __FILE__),
[],
null,
true
);
wp_enqueue_style(
'pem-articles-table',
plugins_url('assets/css/table.css', __FILE__)
);
});
À ce stade, le plugin ne fait encore rien de visible. Pourtant, le socle est en place. L’arborescence est claire, le point d’entrée est défini, le shortcode est posé et les assets sont prêts à être utilisés. Le chapitre suivant s’appuiera sur cette base pour exposer concrètement les données via l’API REST WordPress, en donnant un rôle central à wp-json.
Exposer les données avec l’API REST WordPress
Une fois le socle du plugin posé, le cœur du travail peut commencer. Exposer des données via l’API REST n’est pas un détail technique, c’est un choix d’architecture. Il détermine comment les données seront consommées et réutilisées, en permettant de les fournir indépendamment de leur affichage. Le serveur n’a plus pour rôle de produire une interface, mais de répondre à une requête en décrivant un état du système.
Dans notre scénario, le besoin est simple : obtenir la liste des articles du blog avec leurs informations essentielles. En passant par l’API REST, ces données peuvent être consommées par un tableau HTML, mais aussi par un autre script, une autre page, ou une future interface Vue.js, sans modifier la couche serveur.
Définir un endpoint REST rest-endpoints.php
WordPress permet d’enregistrer facilement des endpoints personnalisés, accessibles via le préfixe wp-json, qui constitue la porte d’entrée de l’API. Cette phase d’enregistrement s’appuie sur le hook rest_api_init, déclenché spécifiquement lors de l’initialisation de l’API REST. Dans le plugin, la déclaration se fait dans un fichier dédié afin d’isoler cette responsabilité. Le terme utilisé dans l’URL, ici articles, relève d’un choix sémantique : il décrit ce que l’endpoint expose, sans lien avec le nom du fichier PHP ni avec celui de la fonction.
add_action('rest_api_init', function () {
register_rest_route('pem/v0', '/blog', [
'methods' => 'GET',
'callback' => 'pem_get_articles',
]);
});
Ce code définit une route accessible à l’URL ci-dessous : Le rôle de cette route est clair : fournir des données, rien de plus. Le segment articles sert uniquement à rendre l’URL lisible et explicite pour celui qui consomme l’API, indépendamment de la structure interne du plugin.
/wp-json/pem/v0/blogStructurer la réponse côté serveur
La fonction de rappel associée à l’endpoint doit se concentrer sur une seule chose : préparer une réponse exploitable. Elle interroge WordPress, extrait les informations utiles, puis les retourne sous forme d’un objet de type array, structuré pour être converti en JSON. Le serveur ne décide pas comment ces données seront affichées. Il se contente de décrire les articles, avec un format volontairement simple et lisible.
function pem_get_articles() {
$query = new WP_Query([
// Type de contenu interrogé : ici les articles du blog (post par défaut)
'post_type' => 'post',
// Nombre d’éléments retournés : -1 signifie « tous les articles »
// Dans un cas réel, cette valeur pourra être limitée ou paginée
'posts_per_page' => -1,
]);
$articles = [];
while ($query->have_posts()) {
$query->the_post();
$articles[] = [
'id' => get_the_ID(),
'title' => get_the_title(),
'date' => get_the_date('Y'),
'permalink' => get_permalink(),
'categories' => wp_get_post_categories(get_the_ID(), ['fields' => 'names']),
];
}
wp_reset_postdata();
return $articles;
}Un format JSON pensé pour l’affichage
L’objet de type array retourné par la fonction est automatiquement converti en JSON par WordPress. Ce mécanisme est natif : dès lors qu’un endpoint REST renvoie une valeur PHP sérialisable (array ou objet), WordPress se charge de produire la réponse JSON conforme, sans traitement supplémentaire. Le résultat est une structure directement exploitable côté navigateur. Ce format est pensé pour l’usage. Les clés sont explicites, les données sont déjà prêtes à être affichées, sans transformation complexe côté client.
[
{
"id": 42,
"title": "Titre de l’article",
"date": "2024",
"permalink": "https://example.com/article",
"categories": ["WordPress", "API"]
}
]Tester l’endpoint
Avant toute intégration côté JavaScript, il est essentiel de tester l’endpoint directement. Il suffit d’ouvrir l’URL dans un navigateur ou d’utiliser un outil comme l’inspecteur réseau. Si tout est correctement en place, et que le plugin est bien activé, WordPress renvoie une réponse JSON lisible. Cette étape permet de valider le fonctionnement de l’API indépendamment de toute interface. À ce stade, le serveur a rempli son rôle. Les données sont exposées, accessibles et testables. Le chapitre suivant montrera comment consommer cette API côté navigateur, sans framework, pour transformer ces données en tableau HTML.
https://votresite.com/wp-json/pem/v0/blogCet exemple repose volontairement sur des données publiques, accessibles sans authentification. Les questions de sécurité, de contrôle des accès et d’authentification (cookies, tokens, OAuth, etc.) sont essentielles dès lors que l’on expose des données sensibles ou des actions d’écriture. Elles ne sont pas abordées ici et feront l’objet d’un article dédié.

L’API REST de WordPress ne se limite évidemment pas à la lecture des données. Elle permet également de créer, modifier ou supprimer des contenus via d’autres méthodes
HTTPcommePOST,PUTouDELETE. Dans le cadre de cet article, nous avons toutefois fait le choix de nous concentrer uniquement sur les requêtes de typeGET, afin de clarifier le flux de données et le rôle de chaque couche, sans introduire de complexité supplémentaire.
Consommer l’API sans framework
Une fois l’endpoint REST en place et testé, le travail se déplace naturellement côté navigateur. Dans ce premier article, il ne s’agit pas de construire une interface sophistiquée, mais de consommer l’API, de gérer les états courants, puis de transformer les données reçues en HTML lisible. Cette étape repose uniquement sur les outils natifs du navigateur. Aucun framework n’est nécessaire. L’objectif est de comprendre le cheminement des données, du serveur jusqu’à l’affichage.
Appeler l’API avec fetch() depuis table.js
Pour interroger l’endpoint REST, nous utilisons fetch(), l’API JavaScript moderne dédiée aux requêtes HTTP asynchrones. Ces appels sont placés dans le fichier JavaScript du plugin (assets/js/table.js), chargé côté front via plugins_url('assets/js/table.js', __FILE__). Son fonctionnement et son évolution sont détaillés dans l’article « L’évolution des requêtes HTTP et des opérations asynchrones » publié sur Puce & Média.
const endpoint = `${window.location.origin}/wp-json/pem/v0/blog`;
fetch(endpoint)
.then(response => response.json())
.then(data => {
renderTable(data);
});
La requête appelle simplement l’URL de l’API. La réponse, renvoyée au format JSON, est automatiquement convertie en objet JavaScript exploitable. Le conteneur, préparé par le shortcode, sert de point d’ancrage pour l’affichage. La fonction renderTable, chargée de transformer ces données en HTML, sera détaillée plus loin dans ce chapitre.

Gérer les états satellites de la requête
Une requête HTTP ne se résume pas à un aller‑retour entre le navigateur et le serveur. Autour de l’appel principal gravitent plusieurs états satellites : chargement en cours, réponse invalide, erreur réseau ou problème de traitement. Cette gestion parallèle permet de traiter, sans complexifier le flux principal, les différents états liés à la requête : attente, échec réseau, réponse serveur invalide ou erreur de traitement.
const container = document.getElementById('pem-articles-table');
// État initial : indication de chargement
container.textContent = 'Chargement en cours…';
fetch(endpoint)
.then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP : ${response.status}`);
}
return response.json();
})
.then(data => {
renderTable(data);
})
.catch(error => {
container.textContent = 'Impossible de récupérer les données.';
console.error(error);
});
Transformer les données en HTML
Une fois les données reçues, elles peuvent être transformées en HTML. Ici, nous générons un tableau simple, sans logique avancée de filtrage ou de tri. Chaque entrée de l’objet JavaScript correspond à une ligne du tableau. Le navigateur se charge de l’intégralité de la mise en forme.
function renderTable(articles) {
const table = document.createElement('table');
table.innerHTML = `
<tr>
<th>Titre</th>
<th>Année</th>
<th>Catégories</th>
</tr>
`;
articles.forEach(article => {
const row = document.createElement('tr');
row.innerHTML = `
<td><a href="${article.permalink}">${article.title}</a></td>
<td>${article.date}</td>
<td>${article.categories.join(', ')}</td>
`;
table.appendChild(row);
});
container.appendChild(table);
}
À ce stade, l’objectif est atteint. Les données exposées par l’API REST sont consommées, transformées et affichées, sans framework et sans logique complexe. Le chapitre suivant montrera comment cette même API pourra être exploitée par une couche Vue.js, chargée uniquement de l’affichage avancé, du filtrage et de la gestion d’état.
Ce que cette première version permet déjà
Cette première version du plugin remplit pleinement son rôle. Elle permet déjà de tester concrètement l’exposition et l’affichage de l’ensemble des données, en s’appuyant sur une architecture simple, lisible et maîtrisée, qui peut être comprise et maintenue sans dépendance excessive. Le plugin est réutilisable. L’API exposée n’est pas liée à une page précise ni à un affichage particulier. Elle peut être consommée ailleurs, par un autre script, un autre thème ou un autre projet, sans modification de la couche serveur.
Le shortcode reste propre. Il ne contient aucune logique métier ni de génération de contenu complexe. Il se limite à son rôle d’ancrage et de déclencheur, ce qui le rend facile à intégrer, à déplacer ou à remplacer. Il nous appartient ensuite, en tant que lecteurs et développeurs, de l’enrichir par des paramètres afin d’adapter le comportement à nos besoins. Par exemple, le shortcode peut accepter des arguments pour limiter le nombre d’articles, restreindre une catégorie, ou modifier l’ordre d’affichage. Ces paramètres sont ensuite transmis au code PHP sous forme d’arguments.
add_shortcode('pem_articles_table', function ($atts) {
$args = shortcode_atts([
// Nombre d’articles à retourner
'limit' => -1,
// Catégorie à filtrer (slug)
'category' => '',
// Ordre de tri
'order' => 'DESC',
], $atts);
// $args pourra ensuite être utilisé pour ajuster la requête
return '<div id="pem-articles-table"></div>';
});Cette approche permet de conserver un shortcode simple tout en offrant des points d’extension clairs, sans alourdir la logique ni remettre en cause l’architecture posée. L’API est exploitable en dehors du contexte initial. Les données exposées via wp-json peuvent servir à alimenter d’autres interfaces, des outils internes ou des traitements automatisés. Le plugin devient ainsi une source de données, et non un simple générateur d’affichage.
Enfin, cette version pose une base saine pour évoluer. Les responsabilités sont séparées, les fichiers sont identifiés, et chaque couche peut évoluer sans remettre en cause l’ensemble.
La feuille de style s’inscrit dans cette même logique de sobriété. Le fichier table.css pose une base minimale de mise en forme, suffisante pour rendre le tableau lisible et exploitable, sans introduire de logique avancée. Cette feuille de style ne cherche pas à gérer le tri, le filtrage ou la mise en page avancée. Elle établit simplement un socle visuel neutre, destiné à être complété ou remplacé lorsque l’interface gagnera en complexité.
#pem-articles-table table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
#pem-articles-table th,
#pem-articles-table td {
padding: 0.5rem 0.75rem;
border-bottom: 1px solid #ddd;
text-align: left;
vertical-align: top;
}
#pem-articles-table th {
font-weight: 600;
}Conclusion
Cette version fonctionne. Elle permet de comprendre le flux complet, de l’exposition des données à leur affichage, et de valider une approche orientée API, sans framework.
Mais elle montre aussi rapidement ses limites dès que l’on souhaite filtrer, trier ou maintenir un état plus complexe côté interface. Ces besoins ne relèvent plus du serveur ni de l’API, mais de la couche de présentation. Il est important de rappeler que cette approche se limite volontairement à des usages en lecture et à des données publiques. L’écriture via l’API REST, tout comme les mécanismes de sécurisation et d’authentification, relèvent d’autres problématiques et d’autres choix d’architecture, qui seront abordés séparément.
L’article suivant s’inscrira naturellement dans cette continuité. Il s’appuiera sur la même API, inchangée, pour confier l’affichage et les interactions à une couche dédiée. Vue.js sera retenu pour illustrer cette étape, mais il ne s’agit pas d’un choix exclusif. Toute autre solution front‑end capable de consommer une API REST pourrait remplir ce rôle. Ce qui compte, ce n’est pas l’outil, mais la clarté de l’architecture posée en amont.
