Skip to Content

Terrain 17 — REST API & headless

⏱️ TL;DR — 25 mini-jeux ⭐⭐⭐⭐→⭐⭐⭐⭐⭐ sur ton terrain de prédilection : consommer WordPress via l’API REST (et WPGraphQL) depuis un front Next.js. Explorer /wp-json, créer des endpoints, authentifier, faire du SSG/ISR, des previews. C’est la compétence rare et premium de la Partie 9 et du projet Niveau 4.

💡 Pour un dev React — 90 % est un simple fetch vers une API. Les 10 % « accent WordPress » : pagination par headers, auth par Application Password, invalidation de cache.


🕹️ Mini-jeu 1 — Explorer /wp-json ⭐⭐⭐ 💻

But : découvrir l’API. Mission : ouvre https://ton-site.local/wp-json/ dans le navigateur.

✅ Solution

La racine /wp-json/ liste les namespaces (wp/v2, oembed/1.0, + ceux des plugins). L’API REST est activée par défaut. Tu explores un contrat de données auto-décrit.


🕹️ Mini-jeu 2 — Lister les articles en JSON ⭐⭐⭐ 💻

But : la route de base. Mission : ouvre /wp-json/wp/v2/posts.

✅ Solution

/wp/v2/posts renvoie les articles publiés en JSON. Idem /pages, /media, /categories, et tes CPT show_in_rest (/wp/v2/wpr_book, terrain 15). Ton front consommera exactement ça.


🕹️ Mini-jeu 3 — Paramètres de requête ⭐⭐⭐⭐ 💻

But : filtrer/alléger la réponse. Mission : teste ?per_page=5&_fields=id,title&_embed. Étapes :

/wp-json/wp/v2/posts?per_page=5&_fields=id,title&_embed

✅ Solution

per_page, search, categories, orderby filtrent ; _fields limite les champs (payload plus léger) ; _embed inclut les ressources liées (auteur, image à la une) en une requête. Optimiser la requête = front plus rapide.


🕹️ Mini-jeu 4 — La pagination par headers ⭐⭐⭐⭐ 💻

But : le piège classique. Mission : repère X-WP-Total et X-WP-TotalPages dans les en-têtes de réponse. Étapes :

const res = await fetch(url); const totalPages = Number(res.headers.get('X-WP-TotalPages'));

✅ Solution

Le total n’est pas dans le corps JSON mais dans les headers X-WP-Total/X-WP-TotalPages. Beaucoup s’y cassent les dents. Lis-les pour paginer côté front. Accent WordPress à connaître.


🕹️ Mini-jeu 5 — S’authentifier (Application Password) ⭐⭐⭐⭐⭐ 💻

But : lire des données protégées. Mission : appelle une route authentifiée avec un Application Password (terrain 10). Étapes :

const auth = 'Basic ' + Buffer.from(`user:APP_PASSWORD`).toString('base64'); const res = await fetch(`${WP}/wp-json/wp/v2/posts?status=draft`, { headers: { Authorization: auth } });

✅ Solution

L’Application Password (terrain 10, jeu 9) s’utilise en Basic Auth. ⚠️ Côté serveur uniquement (route handler Next.js) — jamais dans le bundle client. C’est la clé pour lire brouillons/données privées en headless.


🕹️ Mini-jeu 6 — Créer un endpoint custom ⭐⭐⭐⭐⭐ 💻

But : une API sur mesure. Mission : expose GET /wp-json/wpr/v1/books. Étapes :

add_action( 'rest_api_init', function () { register_rest_route( 'wpr/v1', '/books', array( 'methods' => 'GET', 'callback' => 'wpr_rest_books', 'permission_callback' => '__return_true', ) ); } );

✅ Solution

register_rest_route (sur rest_api_init) crée ta route dans ton namespace (wpr/v1). Ici lecture publique assumée (__return_true justifié). Un endpoint dédié renvoie exactement les champs voulus, plus propre que /wp/v2 générique.


🕹️ Mini-jeu 7 — Le callback qui renvoie du JSON ⭐⭐⭐⭐⭐ 💻

But : formater la réponse. Mission : renvoie une liste de livres épurée. Étapes :

function wpr_rest_books( $req ) { $posts = get_posts( array( 'post_type' => 'wpr_book', 'numberposts' => 20 ) ); return array_map( fn( $p ) => array( 'id' => $p->ID, 'titre' => get_the_title( $p ), 'prix' => get_post_meta( $p->ID, 'wpr_prix', true ), ), $posts ); }

✅ Solution

On retourne un tableau → WordPress le sérialise en JSON. On choisit les champs exposés (pas de fuite de meta privées, terrain 16). Utilise $req->get_param('genre') pour filtrer. Contrat de données maîtrisé.


🕹️ Mini-jeu 8 — Protéger l’endpoint ⭐⭐⭐⭐⭐ 💻

But : ne pas tout ouvrir. Mission : exige une capability sur une route d’écriture. Étapes :

'permission_callback' => fn() => current_user_can( 'edit_posts' ),

✅ Solution

Écriture → jamais __return_true. On vérifie la capability (terrain 16, jeu 9). L’API contourne l’UI, pas les permissions. Écris permission_callback explicitement, même pour une lecture publique (choix conscient).


🕹️ Mini-jeu 9 — Exposer une meta (rappel) ⭐⭐⭐⭐ 💻

But : enrichir /wp/v2. Mission : vérifie que wpr_prix apparaît dans /wp/v2/wpr_book.

✅ Solution

Grâce à register_post_meta( ..., 'show_in_rest' => true ) (terrain 15, jeu 6), la meta est dans la réponse standard. Alternative : register_rest_field pour un champ calculé/formaté. Deux façons d’ouvrir tes données au front.


🕹️ Mini-jeu 10 — Configurer CORS ⭐⭐⭐⭐⭐ 💻

But : autoriser ton front. Mission : comprends quand CORS est nécessaire et comment l’ajuster. Étapes :

add_action( 'rest_api_init', function () { // n'ouvrir qu'à ton domaine front, pas '*' } );

✅ Solution

Si le front (Next.js) et WordPress sont sur des domaines différents et que le fetch est côté navigateur, CORS s’applique. En headless bien fait, on fetch côté serveur (pas de CORS). Si besoin, autorise ton origine précise, jamais * en écriture.


🕹️ Mini-jeu 11 — Fetch depuis Next.js ⭐⭐⭐⭐⭐ 💻

But : consommer côté serveur. Mission : liste les livres dans un Server Component. Étapes :

async function Books() { const res = await fetch(`${process.env.WP_API}/wp-json/wpr/v1/books`); const books = await res.json(); return <ul>{books.map(b => <li key={b.id}>{b.titre}</li>)}</ul>; }

✅ Solution

Dans l’App Router, un Server Component async fetch directement — WordPress n’est qu’une API. Ton expertise React s’applique telle quelle. L’URL vient d’une variable d’environnement, pas en dur.


🕹️ Mini-jeu 12 — SSG avec generateStaticParams ⭐⭐⭐⭐⭐ 💻

But : pré-générer les pages. Mission : pré-rends une page par livre. Étapes :

export async function generateStaticParams() { const res = await fetch(`${process.env.WP_API}/wp-json/wpr/v1/books`); const books = await res.json(); return books.map(b => ({ slug: String(b.id) })); }

✅ Solution

generateStaticParams génère les routes au build (SSG) → pages ultra-rapides servies en statique. WordPress ne reçoit aucun trafic visiteur. Combine avec generateMetadata pour le SEO par livre.


🕹️ Mini-jeu 13 — ISR (revalidation) ⭐⭐⭐⭐⭐ 💻

But : rafraîchir sans rebuild. Mission : revalide une page toutes les 60 s. Étapes :

const res = await fetch(url, { next: { revalidate: 60 } });

✅ Solution

revalidate: 60 régénère la page en arrière-plan au plus toutes les 60 s (ISR) : un livre publié finit par apparaître, sans rebuild complet. Tu dois savoir expliquer le délai au client (attente vs fraîcheur).


🕹️ Mini-jeu 14 — Prévisualiser un brouillon ⭐⭐⭐⭐⭐ 💻

But : preview éditoriale. Mission : affiche un brouillon via Draft Mode + Application Password.

✅ Solution

Next.js Draft Mode bascule une route en rendu dynamique ; le fetch inclut alors l’auth (Application Password) pour lire status=draft. L’éditeur voit son brouillon rendu par le front sans publier. Confort client majeur (terrain 03, jeu 27).


🕹️ Mini-jeu 15 — Revalidation à la demande ⭐⭐⭐⭐⭐ 💻

But : mettre à jour instantanément. Mission : déclenche revalidatePath depuis un hook save_post. Étapes :

// WordPress : au save, ping la route Next.js add_action( 'save_post_wpr_book', fn() => wp_remote_post( NEXT_HOOK_URL, array( /* secret */ ) ) );

✅ Solution

À la place de l’ISR temporel, un webhook save_post → route /api/revalidate (protégée par secret) → revalidatePath() met à jour immédiatement la page concernée. Plus frais, plus de plomberie. Choix d’architecture (terrain projet 4).


🕹️ Mini-jeu 16 — Installer WPGraphQL ⭐⭐⭐⭐ 💻

But : l’alternative typée. Mission : installe WPGraphQL et ouvre son IDE.

✅ Solution

WPGraphQL ajoute un endpoint /graphql + un IDE (GraphiQL) dans l’admin. Tu demandes exactement les champs voulus en une requête (fini le sur-fetch REST). Familier si tu viens d’Apollo/urql.


🕹️ Mini-jeu 17 — Une requête GraphQL ⭐⭐⭐⭐⭐ 💻

But : interroger en GraphQL. Mission : récupère titres + prix des livres. Étapes :

{ books(first: 10) { nodes { title } } }

✅ Solution

Une seule requête, champs choisis, imbrication (auteur, image) sans _embed. Pour exposer un CPT/meta en GraphQL, on l’enregistre avec show_in_graphql (ou via l’API WPGraphQL). Puissant pour un front complexe.


🕹️ Mini-jeu 18 — REST ou GraphQL ? ⭐⭐⭐⭐ 💻

But : choisir en connaissance. Mission : liste 2 avantages de chacun.

✅ Solution

REST : natif (zéro plugin), simple, cacheable par URL. GraphQL : une requête, champs précis, imbrication (moins d’aller-retours). REST pour du simple/natif ; GraphQL pour un front riche à données imbriquées. Tu sais argumenter les deux (Partie 9).


🕹️ Mini-jeu 19 — Mettre en cache la réponse ⭐⭐⭐⭐⭐ 💻

But : soulager WordPress. Mission : cache la sortie de /wpr/v1/books avec un transient.

✅ Solution

Côté WordPress, un transient (terrain 15, jeu 23) évite de recalculer la liste à chaque appel. Côté Next.js, revalidate cache la page. Deux niveaux de cache : back (transient) et front (ISR). Invalider au save.


🕹️ Mini-jeu 20 — Restreindre l’API (sécurité) ⭐⭐⭐⭐⭐ 💻

But : ne pas trop exposer. Mission : exige l’authentification pour lister les utilisateurs via REST.

✅ Solution

/wp/v2/users peut divulguer des identifiants d’auteurs. On la restreint via un filtre rest_authentication_errors (exiger la connexion) ou un plugin. Principe : n’expose en public que ce qui doit l’être (terrain 16).


🕹️ Mini-jeu 21 — Gérer les erreurs côté front ⭐⭐⭐⭐ 💻

But : robustesse. Mission : gère un WordPress lent/indisponible dans le fetch Next.js. Étapes :

const res = await fetch(url, { next: { revalidate: 60 } }); if (!res.ok) throw new Error('WP indisponible');

✅ Solution

Un front headless doit survivre à un back momentanément KO : vérifier res.ok, try/catch, page d’erreur, éventuel fallback au cache. Un site statique reste debout même si WordPress redémarre — un avantage du headless à mettre en avant.


🕹️ Mini-jeu 22 — Variables d’environnement ⭐⭐⭐⭐ 💻

But : ne rien coder en dur. Mission : mets l’URL WP et l’Application Password en .env. Étapes :

WP_API=https://cms.exemple.com WP_APP_USER=headless WP_APP_PASS=xxxx xxxx xxxx

✅ Solution

URL et secrets vivent dans l’environnement (.env.local, jamais commité). Les secrets ne sont lus que côté serveur. Hygiène identique à tes autres projets Next.js.


🕹️ Mini-jeu 23 — Schéma d’architecture headless ⭐⭐⭐⭐ 💻

But : visualiser le tout. Mission : dessine le flux back ↔ front.

✅ Solution

WordPress = back-office + source de données ; Next.js = front rapide qui la consomme et se revalide. C’est HeadlessShelf (Partie 9).


🕹️ Mini-jeu 24 — Un mini front complet ⭐⭐⭐⭐⭐ 💻

But : assembler. Mission : un Next.js avec /livres (SSG) et /livres/[id] (ISR) qui consomme /wpr/v1/books.

✅ Solution

Liste en SSG (generateStaticParams), détail en ISR (revalidate), fetch côté serveur, gestion d’erreur, secrets en .env. Tu tiens un vrai front headless — le projet Niveau 4 en condensé. Ta valeur ajoutée premium.


🕹️ Mini-jeu 25 — Récap headless ⭐⭐⭐⭐⭐ 💻

But : capstone du terrain. Mission : endpoint custom sécurisé + auth Application Password + SSG/ISR + preview Draft Mode + revalidation on-demand + cache back, le tout documenté.

✅ Solution

Tu couvres toute la chaîne headless : exposer proprement, authentifier, pré-générer, prévisualiser, invalider. C’est la compétence rare qui se facture cher en freelance (Partie 13). WordPress + Next.js : ton pont, ton avantage. Dernier terrain : WooCommerce (18).


Terrain suivant : 18 — WooCommerce.