« Pourquoi mon service externe reçoit-il deux notifications pour chaque article importé, alors que le fichier source n’en contient qu’une par ligne ? » C’est le symptôme signalé après la mise en place d’un import de contenu via l’API REST de WordPress : chaque article déclenche deux appels à la fonction accrochée sur rest_after_insert_post, ce qui double les envois vers un service de notification externe.
Symptôme
Le journal applicatif affiche, pour un seul article importé, deux lignes strictement identiques :
[14-Oct-2023 13:12:03] Notification envoyée pour l’article 4821
[14-Oct-2023 13:12:03] Notification envoyée pour l’article 4821
Le code accroché au hook ne contient pourtant aucune boucle visible, et l’article n’apparaît qu’une seule fois dans la base de données, avec un identifiant unique.
Diagnostic

La première étape consiste à ajouter une trace complète de la pile d’appels dans le callback, pour identifier d’où provient chaque déclenchement :
add_action( 'rest_after_insert_post', function( $post, $request ) {
error_log( $request->get_method() . ' ' . $request->get_route() );
error_log( wp_debug_backtrace_summary() );
}, 10, 2 );
Le journal révèle alors deux appels distincts et légitimes :
POST /wp/v2/posts
PATCH /wp/v2/posts/4821
Le script d’import réalise en réalité deux requêtes HTTP par article : une première requête POST pour créer l’article avec son contenu principal, suivie d’une seconde requête PATCH pour lui associer ses catégories et sa fonctionnalité de mise en avant, une fois l’identifiant de média obtenu par un appel intermédiaire. Le hook rest_after_insert_post se déclenche fidèlement à chacun de ces deux appels du contrôleur REST, car il ne fait aucune distinction entre une création et une mise à jour : il s’exécute à chaque fois que le contrôleur termine son traitement d’écriture, quelle que soit la méthode HTTP utilisée.
Correctif
Le hook ne peut pas être blâmé : c’est la logique du callback qui doit distinguer les deux cas, à l’aide de la méthode HTTP transmise par l’objet WP_REST_Request :
add_action( 'rest_after_insert_post', function( $post, $request ) {
if ( 'POST' !== $request->get_method() ) {
return; // On ignore les mises à jour, seule la création nous intéresse.
}
notifier_service_externe( $post->ID );
}, 10, 2 );
Cette condition suffit à ramener le nombre de notifications à une seule par article, indépendamment du nombre d’appels REST successifs effectués par le script d’import pour compléter la fiche.
Une alternative : un drapeau de traitement en métadonnée
Quand la méthode HTTP seule ne suffit pas à distinguer clairement les cas, par exemple si le script d’import réalise plusieurs PATCH successifs et qu’un seul doit déclencher la notification finale, une métadonnée de suivi explicite reste la solution la plus robuste :
add_action( 'rest_after_insert_post', function( $post, $request ) {
if ( 'oui' === get_post_meta( $post->ID, 'notification_envoyee', true ) ) {
return;
}
if ( empty( get_post_meta( $post->ID, 'import_termine', true ) ) ) {
return; // L'import n'a pas encore marqué la fiche comme complète.
}
notifier_service_externe( $post->ID );
update_post_meta( $post->ID, 'notification_envoyee', 'oui' );
}, 10, 2 );
Cette approche déplace la responsabilité du déclenchement vers un signal métier explicite (« l’import est terminé pour cette fiche »), plutôt que de déduire ce signal de la méthode HTTP utilisée, ce qui reste fragile si le script d’import évolue.
Prévention
- Documenter, dans le script d’import lui-même, le nombre exact d’appels REST effectués par élément importé : cette information évite bien des sessions de débogage ultérieures.
- Ne jamais supposer qu’un hook d’écriture REST correspond à une création unique : toujours vérifier
$request->get_method()avant de déclencher un effet de bord coûteux ou irréversible, comme un envoi vers un service tiers. - Pour un import volumineux, préférer un script WP-CLI qui appelle directement
wp_insert_post()avec toutes les données déjà rassemblées en un seul appel, plutôt que de multiplier les requêtes REST successives, ce qui réduit à la fois le nombre d’appels réseau et le risque de déclenchement multiple d’un même hook.
Pour aller plus loin
Ce type de symptôme illustre une règle plus générale : un hook REST reflète fidèlement la séquence d’appels du client, pas une intention métier. Dès qu’un import ou une synchronisation externe s’appuie sur plusieurs requêtes successives pour construire une même fiche, tout code accroché à ces hooks doit explicitement gérer cette pluralité d’appels, sous peine de déclencher des effets de bord en double sans qu’aucune ligne de code ne soit, à proprement parler, fautive.