# register_rest_field pour enrichir une réponse API existante sans la dupliquer

> Ajouter un champ calculé à un endpoint REST natif plutôt que de créer un endpoint parallèle qui recopie déjà toute la logique existante.

- Auteur : Clément Hadrot
- Publié le : 2025-07-01
- Mis à jour le : 2025-07-01
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/register-rest-field-enrichir-reponse-api-existante/

## L’essentiel

- register_rest_field ajoute un champ sans toucher au schéma de base
- Le callback get_callback reçoit l'objet déjà préparé pour la réponse
- Un endpoint parallèle finit toujours par diverger du natif

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

> L'essentiel à retenir : register_rest_field ajoute un champ sans toucher au schéma de base ; Le callback get_callback reçoit l'objet déjà préparé pour la réponse ; Un endpoint parallèle finit toujours par diverger du natif

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 ?
