# register_rest_field : ajouter vos champs aux réponses de l’API REST

> Besoin d'un champ calculé ou d'une méta dans vos réponses REST sans créer de route entière ? register_rest_field fait exactement ça, proprement.

- Auteur : Clément Hadrot
- Publié le : 2020-04-01
- Mis à jour le : 2020-04-01
- Catégorie : Headless &amp; API
- URL : https://wpmoderne.dev.wordpress-developpement.fr/headless/register-rest-field-ajouter-champs-reponses-api/

## L’essentiel

- Ajouter un champ sans dupliquer une route
- get_callback et update_callback séparés
- Schéma déclaré pour la documentation

Sur un projet e-commerce headless, il fallait afficher le prix affiché sur la fiche produit du front, une valeur calculée à partir d'une méta `_prix_ht` et d'un taux de TVA stocké ailleurs. Créer une route REST entière pour ça aurait été disproportionné : la fonction `register_rest_field()` existe précisément pour ce cas, ajouter un champ à une réponse existante sans réécrire le contrôleur.

Elle s'utilise sur n'importe quel type d'objet déjà exposé par l'API : articles, pages, types personnalisés, taxonomies, utilisateurs, commentaires. Le champ ajouté apparaît alors dans toutes les routes concernées, y compris les listes, ce qui la rend bien plus économique qu'une route sur mesure quand il s'agit simplement d'enrichir une réponse.

## La structure de base

`register_rest_field()` prend trois arguments : le type d'objet concerné, le nom du champ à ajouter, et un tableau de configuration contenant `get_callback`, `update_callback` et `schema`. On l'appelle généralement au hook `rest_api_init`, après l'enregistrement des types de contenu.

```
add_action( 'rest_api_init', function () {
    register_rest_field( 'product', 'prix_ttc', array(
        'get_callback'    => 'monsite_get_prix_ttc',
        'update_callback' => null,
        'schema'          => array(
            'description' => 'Prix TTC calculé à partir du prix HT et du taux de TVA.',
            'type'        => 'number',
            'context'     => array( 'view', 'edit' ),
        ),
    ) );
} );

function monsite_get_prix_ttc( $object ) {
    $prix_ht = get_post_meta( $object['id'], '_prix_ht', true );
    $taux    = 1.20;
    return round( (float) $prix_ht * $taux, 2 );
}
```

Le premier paramètre de la fonction de lecture reçoit le tableau représentant l'objet déjà préparé par le contrôleur REST (avec la clé `id`, entre autres), pas l'objet `WP_Post` complet. C'est une source d'erreur fréquente chez les débutants qui tentent d'appeler `$object->ID` et obtiennent une notice PHP.

## Rendre le champ modifiable avec update_callback

Si le champ doit pouvoir être écrit depuis le front (par exemple une préférence utilisateur stockée en méta), on renseigne `update_callback`. Elle reçoit la valeur envoyée dans la requête, puis l'objet `WP_Post` complet cette fois.

> L'essentiel à retenir : Ajouter un champ sans dupliquer une route ; get_callback et update_callback séparés ; Schéma déclaré pour la documentation

```
function monsite_update_note_interne( $value, $post ) {
    if ( ! current_user_can( 'edit_post', $post->ID ) ) {
        return new WP_Error( 'rest_forbidden', 'Permission refusée.', array( 'status' => 403 ) );
    }
    update_post_meta( $post->ID, '_note_interne', sanitize_text_field( $value ) );
}

register_rest_field( 'post', 'note_interne', array(
    'get_callback'    => function ( $object ) {
        return get_post_meta( $object['id'], '_note_interne', true );
    },
    'update_callback' => 'monsite_update_note_interne',
    'schema'          => array(
        'type'    => 'string',
        'context' => array( 'edit' ),
    ),
) );
```

Notez le contrôle de permission explicite à l'intérieur du callback : `register_rest_field` ne gère pas les permissions pour vous, contrairement à une route déclarée avec `permission_callback`. Oublier cette vérification revient à laisser n'importe quel utilisateur authentifié modifier la méta, même sans droit d'édition sur l'article.

## Documenter le champ avec le schéma

Le tableau `schema` n'est pas obligatoire pour que le champ fonctionne, mais il conditionne son apparition dans la documentation générée par `/wp-json` et dans les outils qui s'appuient dessus (comme la découvrabilité OPTIONS d'une route). Je le renseigne systématiquement : `type`, `description` et `context` suffisent dans la majorité des cas.

### Le rôle du context

- `view` : le champ apparaît dans les réponses publiques standard.
- `edit` : le champ n'apparaît que lorsque la requête précise `context=edit`, généralement réservée aux utilisateurs autorisés (l'éditeur de blocs, par exemple).
- `embed` : le champ apparaît dans les réponses allégées utilisées par `_embed`.

Restreindre un champ sensible au contexte `edit` évite de l'exposer publiquement par erreur, sans avoir à écrire de logique de permission dans `get_callback`.

## Un cas concret : exposer le temps de lecture estimé

Sur un blog headless, j'ai ajouté un champ `temps_lecture` calculé à la volée à partir du nombre de mots du contenu, pour l'afficher sur les vignettes d'articles sans recalcul côté front.

```
register_rest_field( 'post', 'temps_lecture', array(
    'get_callback' => function ( $object ) {
        $post = get_post( $object['id'] );
        $mots = str_word_count( wp_strip_all_tags( $post->post_content ) );
        return (int) ceil( $mots / 200 );
    },
    'schema' => array(
        'type'        => 'integer',
        'description' => 'Temps de lecture estimé en minutes.',
        'context'     => array( 'view' ),
    ),
) );
```

Le calcul reste simple volontairement : 200 mots par minute est une moyenne suffisante pour un affichage indicatif, et le coût de calcul reste négligeable sur une réponse de liste paginée à dix éléments.

## Limites à connaître

Le champ ajouté n'est pas filtrable ni triable via les paramètres de requête standard de l'API REST : on ne peut pas demander `?orderby=prix_ttc` sans ajouter en plus un filtre sur `rest_product_query`. Pour un besoin de tri ou de filtre sur un champ calculé, il faut donc combiner `register_rest_field` avec un filtre de requête, ou stocker la valeur en méta indexable en amont plutôt que de la calculer à la volée.

## Notre verdict

`register_rest_field` est l'outil à utiliser par défaut dès qu'il s'agit d'enrichir une réponse existante avec une donnée calculée ou une méta cachée. Réserver `register_rest_route` aux cas où la donnée n'a pas de rattachement naturel à un objet REST déjà exposé évite de multiplier des routes qui dupliquent en réalité de simples champs.
