Le WordPress d'aujourd'hui, décodé pour les développeurs

Extensions

Créer des endpoints REST personnalisés dans une extension WordPress

register_rest_route, permission_callback et schéma d'arguments : la méthode complète pour exposer des données propres et sécurisées via l'API REST de WordPress.

Par WordPress Développement • 30 septembre 2026 • 7 min de lecture • Aucun commentaire
Créer des endpoints REST personnalisés dans une extension WordPress

Depuis son intégration au cœur de WordPress en version 4.7 fin 2016, l’API REST est devenue le moyen standard d’exposer des données pour une application mobile, un frontend découplé en React ou Vue, ou simplement une intégration avec un service tiers. Beaucoup d’extensions se contentent pourtant de réutiliser les endpoints natifs (/wp/v2/posts, /wp/v2/users) alors qu’un besoin métier précis justifie souvent un endpoint sur mesure, plus simple à consommer et surtout plus facile à sécuriser correctement.

Cet article détaille la création d’un endpoint personnalisé avec register_rest_route(), en insistant sur le point le plus souvent négligé : la fonction permission_callback, dont l’absence génère une alerte de dépréciation depuis WordPress 5.5 et un comportement par défaut dangereux pour la sécurité.

Déclarer une route personnalisée

L'essentiel à retenir : permission_callback n'est pas optionnel en pratique ; Valider les arguments avant de les traiter ; WP_Error normalise les réponses d'erreur

Une route se déclare toujours sur l’action rest_api_init, avec un espace de noms (ici mon-extension/v1), un chemin, et un tableau d’options. Voici un premier endpoint en lecture seule, qui renvoie une réservation à partir de son identifiant :

add_action( 'rest_api_init', function () {
	register_rest_route( 'mon-extension/v1', '/reservations/(?P<id>\d+)', array(
		'methods'             => WP_REST_Server::READABLE,
		'callback'            => 'mon_extension_get_reservation',
		'permission_callback' => function () {
			return current_user_can( 'edit_posts' );
		},
		'args'                => array(
			'id' => array(
				'description'       => 'Identifiant de la réservation.',
				'type'              => 'integer',
				'required'          => true,
				'validate_callback' => function ( $valeur ) {
					return is_numeric( $valeur ) && (int) $valeur > 0;
				},
				'sanitize_callback' => 'absint',
			),
		),
	) );
} );

function mon_extension_get_reservation( WP_REST_Request $request ) {
	$reservation = get_post( $request->get_param( 'id' ) );

	if ( ! $reservation || 'reservation' !== $reservation->post_type ) {
		return new WP_Error(
			'reservation_introuvable',
			'Aucune réservation ne correspond à cet identifiant.',
			array( 'status' => 404 )
		);
	}

	return rest_ensure_response( array(
		'id'     => $reservation->ID,
		'titre'  => get_the_title( $reservation ),
		'statut' => $reservation->post_status,
	) );
}

L’URL finale est /wp-json/mon-extension/v1/reservations/42. Le motif entre parenthèses est une expression régulière : le groupe nommé id devient un paramètre de la requête, que l’on lit avec get_param(). Les constantes de WP_REST_Server (READABLE, CREATABLE, EDITABLE, DELETABLE) évitent d’écrire les méthodes HTTP en toutes lettres.

permission_callback : le point qu’on ne saute pas

Depuis WordPress 5.5, déclarer une route sans permission_callback provoque un avertissement de développement. Ce n’est pas une simple formalité : cette fonction est le seul endroit où vous décidez qui a le droit d’appeler la route. Pour un endpoint réellement public, le choix doit être explicite, avec 'permission_callback' => '__return_true'. Ce choix documente l’intention, et il se relit en revue de code.

Pour une route réservée aux utilisateurs connectés, la fonction vérifie une capacité précise plutôt qu’un simple état de connexion :

function mon_extension_peut_modifier( WP_REST_Request $request ) {
	if ( ! current_user_can( 'edit_post', (int) $request['id'] ) ) {
		return new WP_Error(
			'rest_forbidden',
			'Vous n\'avez pas le droit de modifier cette réservation.',
			array( 'status' => rest_authorization_required_code() )
		);
	}
	return true;
}

La fonction rest_authorization_required_code() renvoie 401 pour un visiteur anonyme et 403 pour un utilisateur connecté mais sans droit suffisant, ce qui permet au client de distinguer « connectez-vous » de « vous n’avez pas le droit ». Attention à un malentendu fréquent : pour les requêtes issues d’un navigateur, l’authentification par cookie exige un nonce transmis dans l’en-tête X-WP-Nonce, généré avec wp_create_nonce( 'wp_rest' ). Sans lui, WordPress considère la requête comme anonyme, même si l’utilisateur est bien connecté. Pour des applications externes, les mots de passe d’application, disponibles depuis WordPress 5.6, évitent d’exposer les identifiants réels.

Valider et assainir les arguments

Les arguments déclarés dans args sont vérifiés avant l’appel de la fonction de rappel. Si la validation échoue, WordPress renvoie lui-même une réponse 400 avec le détail du paramètre fautif : votre code n’est jamais exécuté avec une donnée invalide. Pour une route d’écriture, le schéma peut être plus riche :

register_rest_route( 'mon-extension/v1', '/reservations', array(
	'methods'             => WP_REST_Server::CREATABLE,
	'callback'            => 'mon_extension_creer_reservation',
	'permission_callback' => function () {
		return current_user_can( 'edit_posts' );
	},
	'args'                => array(
		'email'    => array(
			'type'              => 'string',
			'format'            => 'email',
			'required'          => true,
			'sanitize_callback' => 'sanitize_email',
		),
		'personnes' => array(
			'type'    => 'integer',
			'minimum' => 1,
			'maximum' => 12,
			'default' => 2,
		),
		'service'  => array(
			'type' => 'string',
			'enum' => array( 'midi', 'soir' ),
		),
	),
) );

Les mots-clés type, format, minimum, maximum et enum suivent le vocabulaire du JSON Schema, et WordPress les applique de lui-même lorsque vous ne fournissez pas de validate_callback. Une fonction de validation personnalisée reste utile pour les règles métier qu’un schéma ne sait pas exprimer, comme « ce créneau n’est pas déjà complet ».

Des erreurs qui parlent le même langage

Renvoyer false ou un tableau ad hoc pour signaler un problème oblige chaque client à deviner le format. L’objet WP_Error règle ce point : le code d’erreur est une chaîne stable, le message est destiné à l’humain, et le troisième argument porte le statut HTTP. L’API REST convertit l’ensemble en une réponse JSON uniforme, avec les clés code, message et data. Un client qui consomme trois de vos routes apprend une seule fois à lire leurs erreurs.

Un cas concret : la liste des créneaux disponibles

Un restaurant veut afficher dans une application mobile les créneaux encore libres d’un jour donné. Les endpoints natifs renverraient des contenus bruts, à reconstituer côté client. Un endpoint dédié, /creneaux?date=2021-06-18, calcule la disponibilité côté serveur et ne renvoie que l’utile : une liste d’heures et un nombre de places. La route est publique, donc déclarée avec __return_true, mais elle valide rigoureusement la date et limite la plage interrogeable, afin qu’un visiteur ne puisse pas déclencher le calcul sur dix ans de calendrier.

Un endpoint public n’est pas un endpoint sans règles : c’est un endpoint dont les règles sont écrites dans la validation plutôt que dans les droits.

Pièges fréquents

  • Laisser permission_callback de côté en espérant que le comportement par défaut protège la route.
  • Faire confiance à $_GET ou $_POST au lieu de passer par $request->get_param() et le schéma d’arguments.
  • Oublier le nonce pour les appels depuis le navigateur, puis « résoudre » le problème en ouvrant la route à tous.
  • Renvoyer des données sensibles dans une route publique parce que l’objet complet de l’article était « pratique » à retourner.
  • Changer le contrat d’une route déjà utilisée sans passer par un nouvel espace de noms de version.

Tester sa route

Un appel curl suffit pour vérifier le comportement public : curl https://exemple.test/wp-json/mon-extension/v1/reservations/42 doit répondre 401 pour un anonyme si la route est protégée. Avec un mot de passe d’application, l’option --user nom:motdepasse permet de vérifier le cas authentifié. Testez aussi les cas d’échec : un identifiant négatif, un paramètre manquant, un article d’un autre type. C’est là que les failles se cachent.

Conclusion

Un endpoint personnalisé bien construit tient en trois garanties : une permission explicite, des arguments validés avant tout traitement, et des erreurs normalisées par WP_Error. Ces trois éléments coûtent quelques lignes, et ils font la différence entre une route que l’on maintient sereinement et une porte ouverte sur les données du site.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi