Trois lignes suffisent-elles vraiment à exposer un champ personnalisé sur un front découplé ? La réponse tient surtout dans le détail des arguments passés à register_meta(), une fonction souvent utilisée avec un seul paramètre show_in_rest mis à true, sans que le développeur mesure ce que cela ouvre exactement.
Pour un projet headless, où un front en JavaScript consomme l’API REST plutôt que les templates PHP classiques, chaque champ personnalisé exposé devient un point d’accès potentiellement lisible par n’importe qui, y compris un visiteur anonyme qui interroge directement l’URL de l’API sans passer par l’interface prévue.
Un register_meta minimal mais complet
function wpm_enregistrer_meta_produit() {
register_meta(
'post',
'reference_fournisseur',
array(
'object_subtype' => 'produit',
'type' => 'string',
'description' => 'Référence interne fournisseur',
'single' => true,
'show_in_rest' => true,
'auth_callback' => function() {
return current_user_can( 'edit_products' );
},
)
);
}
add_action( 'init', 'wpm_enregistrer_meta_produit' );
Le paramètre object_subtype restreint l’enregistrement au type de contenu produit plutôt qu’à tous les articles du site, ce qui évite qu’un champ pensé pour un catalogue apparaisse aussi sur les pages ou les articles de blog. C’est une nuance souvent oubliée : sans lui, register_meta( 'post', ... ) s’applique à l’ensemble des types de contenus basés sur les articles.
show_in_rest ouvre la lecture, pas forcément l’écriture
Passer show_in_rest à true rend la méta lisible par tout client qui consulte l’endpoint /wp/v2/produit/{id}, y compris sans authentification, dès lors que le contenu lui-même est publié. L’écriture, en revanche, reste conditionnée par auth_callback : sans lui, WordPress applique une vérification par défaut basée sur la capacité edit_post, ce qui peut suffire ou non selon la sensibilité du champ.

Sur un champ qui contient une information interne, comme une marge commerciale ou une référence fournisseur, il est préférable de définir une capacité personnalisée plutôt que de s’appuyer sur la capacité générique d’édition. Cela évite qu’un rôle capable d’éditer le produit sur le front en profite pour modifier un champ qui ne devrait relever que d’un rôle achat.
Structurer la réponse avec un schema
Depuis les versions qui ont suivi l’introduction de show_in_rest, il est possible de fournir un tableau plutôt qu’un simple booléen, afin de définir un schema précis :
'show_in_rest' => array(
'schema' => array(
'type' => 'string',
'description' => 'Référence interne fournisseur',
'pattern' => '^[A-Z0-9-]{4,20}$',
),
),
Ce schema n’est pas décoratif : l’API REST l’utilise pour valider les valeurs entrantes lors d’une requête d’écriture, ce qui évite qu’une chaîne mal formée ou un type inattendu vienne polluer la base de données via l’API plutôt que via l’écran d’édition classique.
Champs multiples et objets imbriqués
Pour un champ qui accepte plusieurs valeurs, il faut passer single à false et adapter le schema en conséquence :
single => falsepour un tableau de valeurs répétéestype => 'array'côté schema, avec un sous-schemaitems- Un
sanitize_callbackdédié si le nettoyage par défaut ne convient pas au format attendu
Éviter l’exposition involontaire d’un champ sensible
Une pratique risquée consiste à copier un enregistrement existant pour un nouveau champ sans relire les valeurs par défaut. Omettre show_in_rest ne suffit pas toujours à protéger une donnée : certaines extensions listent les métadonnées disponibles par un autre canal, ou un développeur ajoute plus tard show_in_rest => true sur un groupe de champs sans vérifier chacun individuellement.
| Argument | Rôle | Risque si oublié |
|---|---|---|
| object_subtype | Restreint au bon type de contenu | Champ visible sur tous les articles |
| auth_callback | Contrôle l’écriture | Écriture ouverte à trop de rôles |
| schema | Valide le format | Valeurs incohérentes en base |
| single | Structure la valeur | Confusion tableau / valeur unique |
Sur un projet headless, chaque champ exposé à l’API doit être justifié par un besoin réel du front, pas ajouté « au cas où » : ce qui n’est pas exposé n’a pas besoin d’être protégé.
Notre verdict
Un register_meta pensé pour un front découplé mérite le même niveau d’attention qu’un endpoint REST personnalisé : restreindre le type de contenu concerné, définir précisément qui peut écrire, et documenter la structure attendue via un schema. Ces quelques lignes supplémentaires évitent des heures de diagnostic le jour où un champ interne se retrouve exposé plus largement que prévu.