Fonctions de transformation
La cartographie extrait des valeurs d'une requête. Une fonction de transformation va plus loin : il s'agit de votre propre code JavaScript, exécuté sur chaque requête avant l'appel à Shopify Flow. Vous pouvez ainsi remodeler le payload, effectuer une recherche, déterminer si la requête justifie ou non l'exécution d'un workflow, ou encore répondre vous-même à l'appelant.
Cette option se trouve dans l'onglet « Fonction » d'un webhook. Si l'éditeur est vide, cela signifie que la fonction est désactivée.
La forme d'une fonction
export default async function transform(payload, ctx) {
// payload - the parsed request body (JSON, form or XML)
// return an object -> it becomes the payload for mapping and Flow
// return null -> nothing is sent to Flow, and nothing counts
return { ...payload, source: "warehouse" }
}Ce que vous offre ctx
ctx.request |
method, contentType, la requête headers (informations confidentielles masquées) et query |
ctx.webhook |
Les URL du webhook : id et name |
ctx.shop |
Votre domaine myshopify |
ctx.log(...) |
Affiche une ligne que vous pouvez lire dans le panneau de test et dans l'historique |
await ctx.fetch(url, init) |
Appelle une API publique |
await ctx.shopify(query, variables) |
Lit les données des boutiques à l'aide d'Admin GraphQL |
ctx.storage |
Conserve les petites valeurs d'une exécution à l'autre (voir ci-dessous) |
ctx.respond(body, options) |
Répond immédiatement à la personne qui appelle |
Remaniement d'un payload
L'utilisation la plus courante : convertir la structure d'un expéditeur en quelques valeurs dont votre workflow a besoin.
export default async function transform(payload) {
const order = payload.data.attributes
return {
orderNumber: String(order.number),
email: order.customer.email.toLowerCase(),
total: Number(order.total_cents) / 100,
}
}Supprimer les requêtes dont vous n'avez pas besoin
La réponse null est renvoyée et la requête s'arrête là : aucune exécution de flux, aucune entrée dans l'historique et rien n'est décompté de votre forfait. L'appelant reçoit la réponse 200 avec le message { "skipped": true }, ce qui l'empêche de réessayer.
export default async function transform(payload, ctx) {
if (payload.status !== "paid" || payload.total < 100) {
ctx.log("skipping", payload.id, payload.status, payload.total)
return null
}
return payload
}Se souvenir de certaines choses entre deux séances
ctx.storage Il s'agit d'un petit système de stockage clé-valeur propre à votre boutique. C'est l'outil qui vous permet de vous demander : « Ai-je déjà vu cela ? » : enregistrez un identifiant lorsqu'un événement se produit, puis ignorez la requête suivante portant le même identifiant, même si elle arrive plusieurs jours plus tard.
| Appel | Est-ce que |
|---|---|
await ctx.storage.get(key) |
La valeur stockée, ou null |
await ctx.storage.set(key, value) |
Enregistre n'importe quelle valeur JSON, en écrasant la valeur existante |
await ctx.storage.delete(key) |
Le supprime ; true s'il existait |
await ctx.storage.list({ prefix, limit, cursor }) |
Noms et tailles des éléments clés, page par page |
export default async function transform(payload, ctx) {
const key = `seen:${payload.id}`
if (await ctx.storage.get(key)) return null
await ctx.storage.set(key, { at: Date.now() })
return payload
}Enrichissement à l'aide des données des boutiques
ctx.shopify exécute des requêtes GraphQL d'administration en lecture seule pour votre boutique ; ainsi, un workflow peut démarrer avec des données dont l'expéditeur n'a jamais disposé. Il n'a accès qu'à ce que vous autorisez : accordez les périmètres d'accès dont vous avez besoin dans la section «** Accès aux données de la boutique** », sous l'onglet « Fonction ». Aucun accès n'est accordé par défaut, et les mutations sont refusées.
export default async function transform(payload, ctx) {
const data = await ctx.shopify(
`query($q: String!) {
productVariants(first: 1, query: $q) { nodes { id title price } }
}`,
{ q: `sku:${payload.sku}` },
)
const variant = data.productVariants.nodes[0]
if (!variant) return null
return { sku: payload.sku, variantId: variant.id, price: variant.price }
}Répondre vous-même à l'appelant
ctx.respond() renvoie immédiatement une réponse, ce qui transforme le webhook en un petit point de terminaison d'API : valider quelque chose, calculer une réponse et renvoyer celle-ci en une seule requête. Shopify Flow continue de fonctionner quel que soit le résultat que vous renvoyez.
export default async function transform(payload, ctx) {
const ok = typeof payload.email === "string" && payload.email.includes("@")
ctx.respond({ accepted: ok }, { status: ok ? 200 : 422 })
return ok ? payload : null
}Tests
Le panneau « Test » de l’onglet « Fonction » exécute le code présent dans l’éditeur, y compris les modifications non enregistrées, sur un exemple de payload. Il affiche la valeur de retour, la réponse que votre fonction enverrait, les champs Shopify Flow qu’elle génère, vos lignes de journalisation et la durée. Aucun événement ne déclenche Shopify Flow, rien n’est enregistré dans l’historique et rien n’est comptabilisé dans votre forfait. Les appels au stockage sont toutefois réels : un test lit et écrit dans le même magasin que les requêtes en production.
Lorsqu'une fonction échoue
Si votre code génère une exception, expire ou renvoie un résultat inutilisable, la requête est rejetée avec le message 422 et l'échec est consigné dans l'historique avec le message d'erreur et vos lignes de journal, ce qui vous permet de comprendre ce qui s'est passé. Corrigez le code et relancez cette entrée : la relance s'effectuera avec la nouvelle version.
Limites
- 5 secondes et 64 Mo par exécution, ainsi que 50 000 caractères de code.
- Aucun paquet npm.
ctx.fetchest la seule solution, et les adresses internes ou privées sont refusées. ctx.shopifyautorise 10 appels par exécution, pour les requêtes uniquement, dans la limite des périmètres d'accès que vous avez accordés.- Chaque exécution qui parvient à Shopify Flow constitue une invocation normale et est prise en compte dans votre forfait. Une requête ignorée ne l'est pas.

