Qu’est-ce qui coûte le plus cher à un développeur qui alimente un front headless : ajouter un champ calculé à une réponse existante, ou maintenir un endpoint parallèle qui refait toute la sérialisation d’un article ? La deuxième option paraît plus simple au départ, mais elle finit systématiquement par diverger du comportement natif — pagination, filtres, permissions — au premier changement non anticipé.
register_rest_field() résout précisément ce problème : elle permet d’ajouter un champ à la réponse d’un endpoint natif, sans dupliquer sa logique de récupération, de pagination ni de gestion des permissions. Ce billet ne traite pas register_meta(), qui répond à un besoin différent — exposer une métadonnée existante — et non à celui d’ajouter un champ entièrement calculé.
Ce que fait exactement register_rest_field
La fonction accepte trois arguments : le ou les types d’objets concernés ('post', un nom de type personnalisé, ou un tableau de plusieurs types), le nom du champ à ajouter dans la réponse, et un tableau d’arguments contenant jusqu’à trois callbacks : get_callback pour la lecture, update_callback pour l’écriture, et schema pour documenter le champ dans le schéma REST exposé.
function enrichir_reponse_produit() {
register_rest_field( 'produit', 'prix_ttc_formate', [
'get_callback' => function ( array $objet_prepare ) {
$prix_ht = (float) get_post_meta( $objet_prepare['id'], '_prix_ht', true );
$prix_ttc = $prix_ht * 1.2;
return number_format( $prix_ttc, 2 ) . ' €';
},
'schema' => [
'description' => 'Prix TTC formaté pour affichage',
'type' => 'string',
],
] );
}
add_action( 'rest_api_init', 'enrichir_reponse_produit' );
Le premier argument reçu par get_callback n’est pas l’objet WordPress brut, mais déjà le tableau préparé par le contrôleur REST pour la réponse — ce qui donne accès à id, slug et aux autres champs déjà sérialisés, sans nouvelle requête à la base pour les récupérer.
Pourquoi un endpoint parallèle finit par diverger

Un développeur pressé crée souvent un endpoint dédié — /mon-app/v1/produits-enrichis — plutôt que d’enrichir l’existant. Ce choix semble plus simple à court terme, mais il oblige à réimplémenter la pagination, les paramètres de filtre (orderby, meta_query exposée), et la gestion des permissions déjà présentes sur l’endpoint natif /wp/v2/produit. À chaque évolution du cœur — un nouveau paramètre de tri, un correctif de sécurité sur les permissions — l’endpoint parallèle doit être mis à jour manuellement, et l’oubli devient probable.
Gérer aussi l’écriture avec update_callback
Si le champ ajouté doit pouvoir être modifié via une requête PUT ou POST sur l’endpoint natif, l’argument update_callback reçoit la valeur soumise et l’objet post complet, permettant d’appliquer la mise à jour à la métadonnée sous-jacente.
register_rest_field( 'produit', 'note_interne', [
'get_callback' => fn( $objet ) => get_post_meta( $objet['id'], '_note_interne', true ),
'update_callback' => function ( $valeur, $post ) {
if ( ! current_user_can( 'edit_post', $post->ID ) ) {
return new WP_Error( 'permission_refusee', 'Modification non autorisée.' );
}
update_post_meta( $post->ID, '_note_interne', sanitize_text_field( $valeur ) );
},
'schema' => [ 'type' => 'string' ],
] );
Un champ calculé qui dépend d’une requête coûteuse
Attention toutefois : get_callback s’exécute pour chaque élément de la réponse, y compris dans une liste paginée. Un calcul qui déclenche une requête SQL supplémentaire par article multiplie le nombre de requêtes par la taille de la page demandée. Pour un champ agrégé coûteux — une moyenne d’avis, un total de ventes — mieux vaut précalculer la valeur lors d’un hook de sauvegarde et la stocker en métadonnée, que la recalculer à chaque lecture de l’API.
add_action( 'save_post_produit', function ( $post_id ) {
$ventes = $GLOBALS['wpdb']->get_var( $GLOBALS['wpdb']->prepare(
"SELECT SUM(quantite) FROM {$GLOBALS['wpdb']->prefix}ventes_produits WHERE produit_id = %d",
$post_id
) );
update_post_meta( $post_id, '_total_ventes', (int) $ventes );
} );
Le champ REST devient alors une simple lecture de métadonnée déjà calculée, sans coût supplémentaire à l’affichage.
Exposer le champ dans la documentation générée
L’argument schema n’est pas décoratif : il détermine si le champ apparaît dans la réponse à une requête OPTIONS sur l’endpoint, ce qui permet à un client headless de découvrir automatiquement les champs disponibles sans consulter une documentation séparée. Omettre cet argument fonctionne, mais prive les consommateurs de l’API de cette découverte automatique — un détail qui compte pour une équipe front qui ne maintient pas elle-même le backend.
Notion à retenir
register_rest_field() répond à un besoin précis : enrichir une réponse existante sans toucher à sa structure de base ni dupliquer sa logique de récupération. Elle s’utilise différemment de register_meta(), qui expose une métadonnée déjà stockée sans calcul additionnel, et différemment d’un contrôleur REST personnalisé complet, réservé aux cas où la ressource exposée n’a tout simplement rien à voir avec un type de contenu existant. Entre ces trois outils, le bon choix dépend d’une seule question : la donnée existe-t-elle déjà dans un endpoint natif, ou faut-il en créer un entièrement nouveau ?