vendredi 25 septembre 2026

À propos

Contact

Headless & API

Sérialiser les métadonnées ACF pour l’API REST sans exposer les champs internes

Un champ ACF technique n'a rien à faire dans une réponse REST publique. Recette pour ne sérialiser que les champs destinés au front, avec register_rest_field bien réglé.

Par Clément Hadrot • 21 septembre 2020 • 5 min de lecture • Aucun commentaire
Sérialiser les métadonnées ACF pour l'API REST sans exposer les champs internes

Un champ ACF nommé _id_fournisseur_interne ou _seuil_alerte_stock n’est pas fait pour atterrir dans une réponse JSON consultée par n’importe qui sur Internet. C’est pourtant ce qui arrive régulièrement quand un développeur active l’option « Afficher dans l’API REST » d’un groupe de champs ACF sans réfléchir au détail de ce qui est exposé. Cette recette explique comment ne sérialiser, champ par champ, que ce qui doit vraiment sortir vers un front headless.

Le problème posé par l’export ACF en bloc

Depuis la version 5.11 d’Advanced Custom Fields, il est possible d’activer show_in_rest au niveau d’un groupe de champs entier. C’est pratique, mais ça expose tout le groupe sous la clé acf de la réponse, sans distinction entre un champ pensé pour le front (un prix, une description) et un champ purement administratif (une note interne, un identifiant de synchronisation avec un ERP). Sur un projet récent, un client nous a signalé qu’un champ de commentaire interne destiné à l’équipe commerciale apparaissait tel quel dans les réponses de l’API consommées par son application mobile.

La recette : un contrôle champ par champ avec register_rest_field

La fonction native register_rest_field() permet d’ajouter, à un type de contenu donné, un champ précis dans la réponse REST, avec un callback de lecture entièrement maîtrisé :

add_action( 'rest_api_init', function () {
    register_rest_field( 'produit', 'infos_front', array(
        'get_callback' => function ( $post ) {
            return array(
                'prix'        => (float) get_field( 'prix', $post['id'] ),
                'description' => get_field( 'description_courte', $post['id'] ),
                'disponible'  => (bool) get_field( 'en_stock', $post['id'] ),
            );
            // Volontairement absent : 'seuil_alerte_stock',
            // 'id_fournisseur_interne', 'note_commerciale'.
        },
        'schema' => array(
            'description' => 'Champs ACF exposés au front public',
            'type'        => 'object',
        ),
    ) );
} );

Le résultat apparaît sous une clé propre, infos_front, à côté de title ou content, et ne contient que ce qui a été explicitement listé dans le tableau retourné. Aucun champ ACF non mentionné ne peut fuiter, même si un futur collègue ajoute un champ de configuration dans le même groupe ACF sans y penser.

L'essentiel à retenir : register_rest_field donne un contrôle total sur ce qui sort ; Un callback dédié évite d'exposer les champs de configuration ACF ; La granularité champ par champ bat l'export en bloc

Désactiver l’export automatique du groupe ACF

Ce contrôle fin n’a de sens que si l’option native d’ACF reste désactivée pour ce groupe. Dans l’écran de configuration du groupe de champs, il suffit de laisser show_in_rest à sa valeur par défaut (désactivée) plutôt que de l’activer, et de tout faire passer par le champ personnalisé décrit ci-dessus. C’est la garantie qu’aucune clé acf parallèle ne vienne dupliquer, en clair, les champs qu’on a justement cherché à filtrer.

Variante : un champ calculé plutôt qu’une simple recopie

Le get_callback n’est pas limité à recopier des valeurs ACF telles quelles. Il peut calculer une valeur dérivée, utile pour éviter d’exposer une logique métier sensible côté front :

'disponible' => (bool) get_field( 'en_stock', $post['id'] )
    && (int) get_field( 'quantite_stock', $post['id'] ) > 0,

Ici, le front reçoit un simple booléen disponible, jamais la quantité réelle en stock ni le seuil d’alerte configuré par l’équipe achats. Le calcul reste entièrement côté serveur, ce qui évite aussi à un front mal intentionné de reconstituer une donnée interne à partir d’une valeur brute.

Autoriser aussi l’écriture, quand c’est nécessaire

Un troisième paramètre optionnel de register_rest_field() accepte un update_callback, pour les cas où le front doit pouvoir modifier certains champs via une requête authentifiée. Ce n’est pas l’objet de cette recette, volontairement centrée sur la lecture, mais il est utile de savoir que la même granularité champ par champ s’applique aussi à l’écriture, sans jamais avoir à ouvrir tout le groupe ACF en modification.

Vérifier concrètement ce qui sort

Après ce type de changement, un simple appel curl permet de confirmer qu’aucun champ interne ne traîne encore dans la réponse :

curl -s "https://exemple.fr/wp-json/wp/v2/produit/42" | jq '.infos_front, .acf'

Si la clé acf renvoie encore quelque chose après ce changement, c’est le signe que l’option d’export du groupe ACF est restée active quelque part et doit être désactivée.

Les pièges classiques

  • Oublier qu’un champ répétable ACF (type repeater) renvoie un tableau : le get_callback doit filtrer chaque sous-champ, pas seulement le champ parent.
  • Confondre get_field() et get_field_object() : le second renvoie aussi la configuration du champ, jamais destinée au front.
  • Laisser le schema vide : sans schéma déclaré, certains clients GraphQL ou générateurs de types TypeScript ne détecteront pas correctement le champ personnalisé.

En résumé

L’option d’export en bloc d’ACF est une facilité, pas une bonne pratique pour un front headless en production. Passer par register_rest_field() avec un callback explicite demande quelques lignes de plus, mais donne un contrôle total et documenté sur ce qui quitte réellement le serveur. C’est le genre de discipline qui évite, des mois plus tard, de découvrir qu’un champ de note interne s’est baladé dans une réponse JSON accessible à n’importe qui.

Partager :

À propos de l'auteur

Clément Hadrot

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

Voir tous ses articles

Dans la même veine

À lire aussi