Chapitre 9.3 — Créer ses endpoints REST
⏱️ TL;DR — Quand les routes natives ne suffisent pas (logique métier, données agrégées, actions custom), on enregistre ses propres routes avec
register_rest_route()sur le hookrest_api_init. Une route déclare : un namespace/version (wpr-books/v1), un chemin, une méthode (GET/POST…), uncallback(la logique) et — obligatoire — unpermission_callback(l’autorisation). On valide/assainit les arguments (args) et on retourne uneWP_REST_Response(ou uneWP_Error). C’est propre, sécurisé, et versionné.
🎯 Objectifs
- Enregistrer une route REST custom.
- Écrire un
callbacket surtout unpermission_callback. - Valider et assainir les arguments.
- Retourner des réponses et des erreurs correctes.
1. Enregistrer une route
add_action( 'rest_api_init', function () {
register_rest_route( 'wpr-books/v1', '/stats', array(
'methods' => 'GET',
'callback' => 'wpr_books_stats',
'permission_callback' => '__return_true', // public (lecture)
) );
} );
function wpr_books_stats( WP_REST_Request $request ) {
$count = wp_count_posts( 'livre' )->publish;
return new WP_REST_Response( array( 'total_livres' => (int) $count ), 200 );
}- Namespace/version :
wpr-books/v1→ route accessible sous/wp-json/wpr-books/v1/stats. Le versionnage (v1) permet d’évoluer sans casser les clients. callback: la logique ; reçoit uneWP_REST_Requestet retourne une réponse.permission_callback: qui a le droit (voir §2).
2. permission_callback : jamais l’oublier
Le permission_callback est obligatoire : il décide qui peut appeler la route.
// Lecture publique
'permission_callback' => '__return_true',
// Réservé aux utilisateurs pouvant éditer des livres
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
// Vérifier un droit sur un objet précis
'permission_callback' => function ( WP_REST_Request $req ) {
return current_user_can( 'edit_post', (int) $req['id'] );
},⚠️ Piège critique —
permission_callbackmanquant ou__return_truepar défaut. Depuis WP 5.5, une route sanspermission_callbackdéclenche un avertissement et est considérée non sécurisée. Ne mettez__return_trueque pour de la lecture publique assumée. Pour toute écriture ou donnée sensible, vérifiez une capability (et l’authentification, ch. 9.2). C’est l’équivalent REST du « on autorise par capability » (ch. 7.7).
3. Valider et assainir les arguments
On déclare les args avec validation/sanitisation — WordPress les applique avant le callback :
register_rest_route( 'wpr-books/v1', '/livres', array(
'methods' => 'GET',
'callback' => 'wpr_books_liste',
'permission_callback' => '__return_true',
'args' => array(
'genre' => array(
'required' => false,
'type' => 'string',
'sanitize_callback' => 'sanitize_title',
),
'nombre' => array(
'default' => 10,
'type' => 'integer',
'sanitize_callback' => 'absint',
'validate_callback' => function ( $v ) { return $v > 0 && $v <= 100; },
),
),
) );
function wpr_books_liste( WP_REST_Request $request ) {
$args = array( 'post_type' => 'livre', 'posts_per_page' => (int) $request['nombre'] );
if ( $request['genre'] ) {
$args['tax_query'] = array( array( 'taxonomy' => 'genre', 'field' => 'slug', 'terms' => $request['genre'] ) );
}
$q = new WP_Query( $args );
$data = array_map( function ( $p ) {
return array(
'id' => $p->ID,
'titre' => get_the_title( $p ),
'prix' => (float) get_post_meta( $p->ID, 'wpr_prix', true ),
);
}, $q->posts );
return rest_ensure_response( $data );
}sanitize_callback nettoie, validate_callback rejette les valeurs invalides (avec une erreur 400 automatique). On récupère les valeurs via $request['param'].
4. Réponses et erreurs
// Succès avec code + en-têtes
$resp = new WP_REST_Response( $data, 200 );
$resp->header( 'X-Total', 42 );
return $resp;
// Erreur normalisée
return new WP_Error( 'wpr_not_found', __( 'Livre introuvable', 'wpr-books' ), array( 'status' => 404 ) );rest_ensure_response()enveloppe un tableau/objet en réponse REST.WP_Erroravecstatusproduit une erreur HTTP propre (code + message JSON) — le client peut la gérer.
💡 Pour un dev React —
register_rest_route= définir un route handler (comme unapp/api/route.tsde Next.js), avecpermission_callback= votre middleware d’auth,args= la validation de schéma (façon zod), etWP_Error= une réponse d’erreur typée. Le versionnage dans le namespace (v1) est la bonne pratique d’API que vous connaissez. Vous exposez de la logique métier WordPress exactement comme vous écririez une API Next.
5. Bonnes pratiques
- Namespace propre et versionné (
monplugin/v1). permission_callbackexplicite sur chaque route (jamais omis).- Valider/assainir tous les arguments (
args+ callbacks). - Ne pas ré-exposer ce que
wp/v2fait déjà : n’écrivez un endpoint que pour de la logique custom ou de l’agrégation. - Échapper ce qui doit l’être ; ne pas fuiter de données privées.
- Documenter le schéma (
'schema' => …) pour l’auto-découverte.
✏️ Exercices
- Quel argument de
register_rest_routeest indispensable pour la sécurité, et que se passe-t-il si on l’omet ? - Vous voulez un endpoint
GET /wpr-books/v1/livres?nombre=5. Comment garantir quenombreest un entier entre 1 et 100 ? - Comment renvoyer une erreur 404 « Livre introuvable » proprement ?
✅ Solution
- Le
permission_callback: il décide qui peut appeler la route. L’omettre déclenche un avertissement (depuis WP 5.5) et laisse la route non sécurisée. On met__return_trueseulement pour de la lecture publique, sinon on vérifie une capability. - Déclarer l’argument dans
argsavectype => 'integer',sanitize_callback => 'absint'et unvalidate_callbackqui retourne($v > 0 && $v <= 100)— WordPress rejette (400) les valeurs invalides avant le callback. return new WP_Error( 'wpr_not_found', __( 'Livre introuvable', 'wpr-books' ), array( 'status' => 404 ) );— produit une réponse d’erreur JSON avec le code HTTP 404.
🧠 Quiz de révision
1. Sur quel hook enregistre-t-on une route REST ?
Sur rest_api_init (via register_rest_route).
2. Que déclare-t-on obligatoirement pour chaque route ?
Un permission_callback (l’autorisation) — en plus de methods et callback.
3. Comment valider/assainir un paramètre ?
Via args avec sanitize_callback (nettoyage) et validate_callback (validation, rejet 400).
4. Comment retourner une erreur HTTP ?
Avec WP_Error en incluant array( 'status' => 4xx/5xx ).
5. Pourquoi versionner le namespace ?
Pour faire évoluer l’API (v1 → v2) sans casser les clients existants.
Chapitre suivant : WPGraphQL.