Déclencheurs personnalisés

Déclencheurs personnalisés

Copier la page

Créez un déclencheur Shopify Flow sur mesure : condition JavaScript, valeurs avant/après et transitions précises, sans événement Shopify requis.

Les déclencheurs intégrés couvrent les changements qui intéressent la plupart des boutiques. Un déclencheur personnalisé prend en charge le reste : vous choisissez un événement d’Shopify, vous écrivez quelques lignes de JavaScript pour déterminer si cet événement est pertinent et quelles données envoyer, et vous obtenez ainsi un déclencheur que vous pouvez utiliser dans Shopify Flow.

Il résout trois problèmes qu’un déclencheur intégré ne peut pas résoudre :

  • N'exécutez cette action que si votre condition est remplie. « Uniquement lorsqu'une commande supérieure à 500 EUR se voit attribuer la balise vip » correspond à une seule ligne de code, au lieu d'un workflow qui s'exécute sur chaque commande puis effectue un filtrage.
  • Déclenchez une action lors d'une transition, et non lors d'un état. Votre code prend en compte la valeur avant et après le changement ; il est donc possible de dire que « le statut est passé de "brouillon" à "actif" » ou que « le stock est passé en dessous de 5 ». Une condition basée sur la valeur actuelle ne permet pas d'exprimer cela.
  • Envoyez uniquement les champs dont vous avez besoin. Adaptez l'événement aux valeurs réellement utilisées par votre workflow, plutôt que de les relire une par une.

Un déclencheur personnalisé n'a même pas besoin d'un événement Shopify : il peut également s'exécuter selon un calendrier et déterminer lui-même ce qui a changé.

Comment ça marche ?

  1. Choisissez le mode de fonctionnement : un événement Shopify ou un calendrier.
  2. Pour un événement, sélectionnez l'événement qu'il traite (n'importe quel événement Shopify que l'application reçoit déjà) ou partez d'un modèle, qui sélectionne l'événement et génère le code correspondant.
  3. Capturez un payload réel. Apportez cette modification dans votre boutique et l'application récupérera le corps exact de l'événement.
  4. Écrivez une transformation : renvoyez un objet à déclencher, ou null pour l'ignorer.
  5. Testez-le avec l'événement capturé et voyez exactement ce que votre workflow recevrait.
  6. Allumez-le.
L'éditeur de déclencheurs personnalisés, avec le nom, le descripteur, le choix entre un événement Shopify et une planification, l'événement et un modèle
Un nouveau déclencheur personnalisé : choisissez ce qui le déclenche, sélectionnez l'événement et, si vous le souhaitez, partez d'un modèle.

Rédaction de la transformation

Votre déclencheur est un module JavaScript qui exporte une fonction nommée transform. Celle-ci reçoit quatre arguments :

  • payload - le corps brut de l'événement Shopify, tel qu'il est reçu.
  • topic - quel événement s'est déclenché, par exemple PRODUCTS_UPDATE.
  • shop - votre domaine MyShopify.
  • ctx - ctx.log(...) affiche des informations dans le panneau de journalisation situé à côté de l'éditeur, ctx.shopify(...) exécute une requête GraphQL d'administration, et ctx.fetch(...) appelle une URL sur l'Internet public.

Ce que vous renvoyez détermine la suite des événements :

  • Renvoyez un objet et le déclencheur se déclenche, en transmettant cet objet.
  • Renvoyez null et l'événement sera ignoré. C'est ainsi que fonctionne le filtrage : il n'y a pas de langage de filtrage distinct à apprendre.
  • Si vous laissez le fichier vide, il se déclenche à chaque événement de ce type.
flow-triggers/high-value-vip-order.jsjavascript
/**
 * Only fire for high-value orders carrying the vip tag.
 */
export async function transform(payload, topic, shop, ctx) {
  const total = parseFloat(payload.total_price || "0");
  const tags = (payload.tags || "").split(",").map(t => t.trim());

  if (total < 500) return null;
  if (!tags.includes("vip")) return null;

  ctx.log("firing for", payload.name, total);

  return {
    orderId: payload.admin_graphql_api_id,
    orderNumber: payload.name,
    total,
    currency: payload.currency,
    customerEmail: payload.email,
  };
}

La valeur avant la modification

Shopify

Dans les sections « Mise à jour du produit », « Mise à jour de la commande » et « **Mise à jour du client **», la page payload._changes répertorie tous les champs suivis qui ont été modifiés lors de cette mise à jour, accompagnés respectivement des liens oldValue et newValue :

  • Produits : title, handle, description, status, vendor, productType, tags
  • Commandes : financialStatus, fulfillmentStatus, tags, note, lineItems et customAttributes.<name>
  • Clients : tags, note, state (ENABLED, DISABLED, INVITED, DECLINED)

Cette liste est vide lorsque la mise à jour ne concerne pas ces champs - une modification du stock, par exemple - ou lorsque l'application accède à l'enregistrement pour la première fois. L'exemple d'événement que vous capturez dans l'éditeur affiche le champ, ce qui vous permet de voir sa structure réelle avant d'y écrire des données.

flow-triggers/product-went-live.jsjavascript
/**
 * Fire only when a product BECOMES active - not on every later edit of an
 * active product, which is what a condition on the current status would do.
 */
export async function transform(payload, topic, shop, ctx) {
  const change = (payload._changes || []).find((c) => c.field === "status");
  if (!change) return null;
  if (change.oldValue !== "draft" || change.newValue !== "active") return null;

  return {
    productId: payload.admin_graphql_api_id,
    title: payload.title,
    oldStatus: change.oldValue,
    newStatus: change.newValue,
  };
}

Les événements plus spécifiques comportent leurs propres valeurs anciennes et nouvelles ; vous n’avez donc pas besoin d’_changes : une modification de prix comporte oldPrice, newPrice et percentChange ; une modification de stock comporte _oldAvailable, _newAvailable et _delta ; une modification de métachamp comporte previousValue en plus de metafield.value.

Commencez à partir d'un modèle

Dans l'éditeur, sous « Fires on », l'option « Partir d'un modèle » sélectionne l'événement et insère du code fonctionnel. Modifiez les constantes en haut, testez, puis enregistrez. Chaque modèle lit une valeur avant et après la modification :

  • Un champ relatif à un produit, à une commande ou à un client est passé d'une valeur à une autre : le statut est passé de « brouillon » à « actif », une commande a été réglée, un compte a été activé.
  • Le prix a baissé de plus de N % : il s'agit d'une véritable baisse de prix, et non d'une simple modification tarifaire.
  • Le stock est passé en dessous d'un seuil : le système se déclenche une seule fois, au moment où le stock franchit cette limite, et non à chaque vente tant que le niveau est déjà bas.
  • La valeur d'un métachamp de produit a dépassé un seuil : une note est passée en dessous de 3, une marge a dépassé 40.
  • Commande réglée par un client à forte valeur ajoutée : le système analyse le montant total des achats effectués par le client dans votre boutique depuis qu'il est client et ne déclenche l'action que si ce montant dépasse le seuil que vous avez défini.
La carte « Transform » de l'éditeur contenant le code d'un modèle dont le nom est payload._changes
Un modèle génère du code fonctionnel. Modifiez les constantes en haut du fichier, testez-le, puis enregistrez-le.

Récupération de données supplémentaires avec ctx.shopify

Les corps des Webhooks ne contiennent que les champs envoyés par Shopify. Si vous avez besoin d'autres informations (nombre de commandes d'un client, stock d'une variante, métachamp, etc.), interrogez directement l'API d'administration depuis votre transformation :

Enrich the event before decidingjavascript
export async function transform(payload, topic, shop, ctx) {
  const data = await ctx.shopify(`
    query($id: ID!) {
      customer(id: $id) { numberOfOrders tags }
    }
  `, { id: payload.customer.admin_graphql_api_id });

  // Only fire for repeat customers
  if (data.customer.numberOfOrders < 5) return null;

  return {
    orderId: payload.admin_graphql_api_id,
    orderCount: data.customer.numberOfOrders,
  };
}

Elle renvoie l'data de la requête et lève une exception si celle-ci comporte des erreurs, de sorte qu'une erreur apparaisse dans votre test plutôt que de ne rien renvoyer sans rien signaler.

Trois limites qu'il est utile de connaître :

  • Jusqu'à 10 appels par exécution. L'enrichissement n'en nécessite qu'une poignée ; au-delà, on parle généralement d'une boucle. Récupérez ce dont vous avez besoin en une seule requête lorsque cela est possible.
  • Elle lit, elle n'écrit pas. Elle utilise les autorisations que vous lui avez accordées, et l'application ne demande jamais que des droits d'accès en lecture. Toute requête concernant les données de commande échouera si l'accès aux données de commande n'est pas accordé sur la page « Autorisations ». Pour modifier un élément dans votre boutique, effectuez l'opération dans les actions de Shopify Flow qui suivent le déclencheur.
  • Les identifiants de votre boutique ne sont jamais transmis à votre code. La requête est exécutée par l'application en votre nom ; il n'y a donc aucun jeton d'accès susceptible d'être divulgué au sein de l'environnement de test.

ctx.fetch(url, options) Cela fonctionne comme la fonction fetch du navigateur pour tout élément accessible sur l'Internet public : le flux de données sur les stocks d'un fournisseur, un taux de change, votre propre API. Les adresses de réseaux internes et privés sont refusées. Si votre code de déclencheur est enregistré sur GitHub, n'y incluez pas de clé API.

Déclencheurs s'exécutant selon un calendrier défini

Shopify Flow Il réagit lorsqu'un événement se produit. Il ne peut pas réagir lorsqu'aucun événement ne se produit, et il ne peut rien détecter en dehors de votre boutique. Pour cela, sélectionnez « Selon un calendrier » dans la section « Ce qui déclenche cette exécution ». Ce type de déclencheur n'est associé à aucun événement Shopify : votre code s'exécute à intervalles réguliers, toutes les 30 secondes jusqu'à une fois par jour, et détermine lui-même ce qui constitue un changement.

Il s'agit de la même fonction transform, mais avec une payload différente et une valeur de retour différente :

  • payload.state - la valeur renvoyée par votre code en tant que state lors de son exécution précédente. null lors de la première exécution.
  • payload.now et payload.lastRunAt - horodatages.
  • { state, events } de retour : le nouvel état à mémoriser (jusqu'à 32 Ko) et une liste d'événements à déclencher (jusqu'à 100 par exécution). Chaque événement déclenche une fois le « Déclencheur personnalisé » ; lresourceId d'un événement, si vous en avez défini une, devient l'identifiant de l'enregistrement dans Shopify Flow. Renvoyez « null lorsqu'il n'y a rien à signaler.

Lors de la première exécution, contentez-vous de mémoriser les données, sans jamais déclencher d'action. Lors de sa première exécution, votre code n'a aucun point de comparaison, donc tout lui paraîtra nouveau. Enregistrez ce que vous constatez et ne renvoyez aucun événement ; ainsi, l'activation du déclencheur n'encombrera jamais vos workflows. Tous les modèles fonctionnent de cette manière.

L'éditeur de déclencheurs personnalisés en mode « Selon un calendrier », avec l'intervalle et le nombre d'exécutions sur 30 jours
Selon un calendrier : choisissez la fréquence - l'éditeur vous indique le nombre total de sessions ainsi générées - et partez d'un modèle de planification.
flow-triggers/supplier-stock-changed.jsjavascript
// Fires when a value at a JSON URL changes, with the value before and after.
const URL = "https://api.example.com/stock/1042"

export async function transform(payload, topic, shop, ctx) {
  const res = await ctx.fetch(URL, { headers: { accept: "application/json" } })
  if (!res.ok) throw new Error("The URL answered with HTTP " + res.status)
  const value = String((await res.json()).stock)

  // The first run only remembers the value.
  const previous = payload.state ? payload.state.value : undefined
  if (previous === undefined || previous === value) return { state: { value } }

  return {
    state: { value },
    events: [{ oldValue: previous, newValue: value }],
  }
}

Modèles pour les déclencheurs programmés, dans la section « Commencer à partir d’un modèle » de la fiche « Planification » :

  • Produit, commande ou client non mis à jour depuis N jours : fiches produit obsolètes, commandes bloquées, campagne de fidélisation. Chaque enregistrement déclenche une action une fois par période d'inactivité, et les éléments déjà en retard au moment de l'activation ne sont pas tous traités en une seule fois.
  • Une valeur hors de Shopify a été modifiée : un flux de stocks fournisseur, un taux de change, une liste de prix, avec la différence et le pourcentage pour les chiffres.
  • Nouvel élément dans un flux RSS ou Atom : actualités d'un fournisseur, flux de listes, page d'état.
  • Aucune commande depuis N heures : un indicateur de santé pour votre boutique. Il se déclenche une fois lorsque les commandes cessent et une fois lorsqu'elles reprennent.

La fonction « Test » exécute votre code une seule fois, sans déclencher aucune action et sans enregistrer l'état, ce qui vous permet de l'exécuter autant de fois que vous le souhaitez.

Utilisation dans Shopify Flow

Chaque déclencheur personnalisé apparaît dans Shopify Flow sous la même appellation : « Déclencheur personnalisé ». Ajoutez-le à un workflow, puis ajoutez une condition indiquant que le « nom du déclencheur » doit correspondre au nom de votre déclencheur.

Ce nom d'identification apparaît sur la page du déclencheur et ne change jamais, même si vous renommez le déclencheur ; ainsi, votre workflow continue de fonctionner.

Chaque clé renvoyée par votre transformation devient un champ du déclencheur, et l'objet complet est également disponible au format JSON si vous préférez l'analyser vous-même.

Conservez le code sur GitHub

Vous pouvez connecter un dépôt GitHub afin que votre code de déclencheur soit soumis au contrôle de version. Les modifications peuvent alors être examinées dans une pull request ; vous pouvez voir qui a modifié quoi et vous pouvez annuler une transformation qui ne fonctionne plus.

Configurez-le sur la page « Développeurs », dans la section « Connexions ». Vous choisissez les dépôts auxquels l'application a accès, et vous pouvez révoquer cet accès depuis GitHub à tout moment.

Une fois le référentiel sélectionné, tous les déclencheurs existants y sont immédiatement enregistrés, et la synchronisation s'effectue dans les deux sens :

Ce que vous faites Que se passe-t-il ?
Créer, modifier ou dupliquer un déclencheur dans l'application Le fichier a été validé dans votre dépôt
Supprimer un déclencheur dans l'application Le fichier est supprimé de votre référentiel
Appliquer une modification à la branche connectée Le code du déclencheur est mis à jour dans l'application

Chaque déclencheur correspond à un fichier portant le nom de son identifiant, qui contient exactement le module que vous voyez dans l'éditeur - sans rien d'autre autour. Cela signifie que vous pouvez l'ouvrir dans votre propre éditeur, l'exécuter et le valider comme n'importe quel autre fichier JavaScript.

Revenir à une version antérieure

Il n'est pas nécessaire de connaître Git pour annuler une modification. Une fois le dépôt connecté, l'éditeur affiche un menu déroulant « Version » répertoriant toutes les versions précédentes de ce fichier, avec leur date et leur auteur. Choisissez-en une : elle s'affiche alors dans l'éditeur en tant que modification non enregistrée, ce qui vous permet de la consulter au préalable. C'est en enregistrant que vous la réintégrez.

Testez pendant que vous modifiez le fichier localement

Si vous modifiez le fichier dans votre propre éditeur, vous pouvez l'exécuter sur l'événement d'exemple capturé sans avoir à l'enregistrer au préalable dans l'application. Utilisez une clé API dotée du niveau d'exécution disponible sur la page « Développeurs » :

Test the file you are editingbash
curl -X POST https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
  -H "Authorization: Bearer ftk_your_key_here" \
  -H "Content-Type: application/json" \
  -d "$(jq -Rn --rawfile c flow-triggers/high-value-vip-order.js '{code:$c}')"

Vous obtenez le même résultat que celui affiché par le bouton « Test » de l'application : si l'événement se déclenche, l'objet de sortie, vos lignes d'ctx.logs et la durée d'exécution.

Cet endpoint ne déclenche aucune action et n'effectue aucune sauvegarde ; il n'utilise pas non plus de quota de forfait. Vous pouvez donc l'exécuter en toute sécurité à chaque sauvegarde effectuée par un observateur de fichier. (Le bouton « Exécuter le test » de l'application déclenche votre workflow Shopify Flow, ce qui vous permet de le suivre de bout en bout ; cette opération est également gratuite.)

Vous pouvez également récupérer le payload d'exemple séparément à l'aide d'une clé de lecture, l'enregistrer localement et exécuter le fichier entièrement hors ligne :

Get the captured sample payloadbash
curl https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
  -H "Authorization: Bearer ftk_your_key_here"
Un déclencheur personnalisé utilise-t-il le volume alloué de mon forfait ?

Oui, et il comptabilise tous les événements qu'il analyse, et pas seulement ceux qui déclenchent une action. Si votre déclencheur est configuré pour détecter les mises à jour de produits et que votre boutique enregistre 60 000 mises à jour de produits par mois, cela représente 60 000 événements, même si votre code ne réagit qu'à 100 d'entre eux.

Nous recevons, dédupliquons et mettons en file d'attente chacun de ces événements, puis nous exécutons votre code dans un environnement isolé (sandbox) - tout cela avant que votre code ne décide de se déclencher ou non. Ce chiffre reflète ce travail.

En d'autres termes : cela coûte le même prix que le déclencheur intégré pour le même événement. Le filtrage ne vous est pas facturé en supplément, et les tests sont toujours gratuits. Un déclencheur programmé est comptabilisé une fois par exécution.

Le dépistage est-il gratuit ?

Oui. Le bouton « Exécuter le test » de l’application et le point de terminaison API /test sont tous deux exemptés, même si le bouton « Exécuter le test » déclenche réellement votre workflow Shopify Flow afin que vous puissiez observer son exécution. Seules les exécutions en direct utilisent votre quota ; vous pouvez donc itérer autant que vous le souhaitez sur une transformation.

Pourquoi la variable « payload._changes » est-elle vide ?

Soit la mise à jour concernait des champs non suivis - une modification du stock ou d'une variante de produit, par exemple - , soit l'application ne dispose pas encore de référence pour cet enregistrement. La référence est enregistrée la première fois que l'application détecte un enregistrement ; par conséquent, la toute première mise à jour d'un enregistrement qu'elle n'a jamais vu ne comporte aucune valeur antérieure. Toutes les mises à jour suivantes en comportent une.

Mon code peut-il modifier les données de ma boutique ?

Non. ctx.shopify s'exécute avec les droits de lecture que vous lui avez accordés, et l'application ne demande jamais d'accès en écriture. C'est un choix délibéré : un déclencheur qui modifie l'enregistrement qu'il surveille se relance lui-même, ce qui provoque une boucle - c'est pour cette raison que l'Shopify « Arrêter les workflows » intervient. Modifiez les données dans les actions du Shopify Flow qui suivent le déclencheur.

Puis-je changer la poignée plus tard ?

Non, et c'est voulu. Votre workflow Shopify Flow effectue un filtrage sur le nom d'identifiant ; par conséquent, le modifier mettrait fin à l'exécution de ce workflow sans avertissement. Vous pouvez renommer le déclencheur comme bon vous semble : le nom d'identifiant reste inchangé.

Ce nom d'utilisateur correspond également au nom de fichier dans votre dépôt GitHub ; il ne change donc jamais non plus.

Que se passe-t-il si mon code comporte un bug ?

L'événement est ignoré et l'erreur est consignée au niveau du déclencheur, ce qui vous permet de voir ce qui s'est mal passé. Une transformation qui échoue ne bloque jamais rien d'autre : vos autres déclencheurs, qu'ils soient personnalisés ou intégrés, continuent de fonctionner sans être affectés.

Où mon code s'exécute-t-il ?

Dans un environnement isolé, distinct du reste de l’application, avec un délai limité et sans accès aux identifiants de votre boutique. Il ne voit que la charge utile de l’événement que vous avez capturée, ainsi que tout ce que vous récupérez via ctx.shopify ou ctx.fetch.

Que se passerait-il si je modifiais le fichier simultanément sur GitHub et dans l'application ?

C'est la dernière action de sauvegarde qui prévaut. La sauvegarde dans l'application valide les modifications dans le fichier, tandis que le push vers la branche connectée écrase le code conservé dans l'application. Si vous travaillez principalement dans votre dépôt, considérez l'éditeur de l'application comme étant en lecture seule afin d'éviter toute surprise.

Un fichier de mon référentiel peut-il créer un nouveau déclencheur ?

Non. Un déclencheur doit également savoir quel événement d'Shopify il surveille, et le fichier ne contient que du code : deviner l'événement risquerait de le relier à la mauvaise fonction. Créez d'abord le déclencheur dans l'application, puis modifiez son fichier comme vous le souhaitez.

Peut-elle se déclencher lors d'événements que l'application ne reçoit pas déjà ?

Non. Un déclencheur personnalisé surveille les événements auxquels l'application est déjà abonnée pour votre boutique, ce qui dépend des autorisations que vous avez accordées. Accordez l'autorisation pour une ressource et ses événements deviendront également disponibles pour les déclencheurs personnalisés. Pour tout autre cas, exécutez-le selon un calendrier défini.

Prochaines étapes