变换函数

映射功能会从请求中提取值。而转换函数的功能更进一步:它是您自定义的 JavaScript 代码,会在每次请求中于 Shopify Flow 之前执行,因此您可以重构请求负载、进行数据查询、判断该请求是否值得触发工作流程,或者直接向调用方返回响应。

它位于 Webhook 的**“函数**”选项卡中。如果编辑器为空,则表示已关闭。

函数的形状

The contractjavascript
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" }
}

ctx能为您提供什么

ctx.request method、contentType、请求 headers(机密信息已屏蔽)以及 query
ctx.webhook 该 Webhook 的 id 和 name
ctx.shop 您的 myshopify 域名
ctx.log(...) 在测试面板和“历史记录”中写入一行可供阅读的内容
await ctx.fetch(url, init) 调用一个公共 API
await ctx.shopify(query, variables) 使用 Admin GraphQL 读取商店数据
ctx.storage 在每次运行之间保留小数值(见下文)
ctx.respond(body, options) 立即接听来电

重构负载

最常见的用途:将发送方的结构转换为工作流程所需的少量值。

Flatten and normalisejavascript
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,
  }
}

删除不需要的请求

返回null,请求就此终止:不会执行Shopify Flow,不会生成历史记录条目,也不会从您的套餐配额中扣除任何配额。调用方将收到200,其中包含{ "skipped": true },因此不会进行重试。

Only paid orders above 100javascript
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
}

在两次跑步之间记住事情

ctx.storage 这是一个属于您网店的小型键值存储。它是用于判断“我之前见过这个吗?”的工具:当事件发生时存储一个 ID,然后跳过携带相同 ID 的下一个请求,即使该请求是在几天后发来的。

致电 是否
await ctx.storage.get(key) 存储值,即null
await ctx.storage.set(key, value) 商店任何 JSON 值,并覆盖原有内容
await ctx.storage.delete(key) 将其删除;如果存在,则执行:true
await ctx.storage.list({ prefix, limit, cursor }) 各页的关键名称和尺寸
Skip events you have already seenjavascript
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
}

利用商店数据进行数据增强

ctx.shopify 该功能会为您的商店执行只读的 Admin GraphQL 查询,因此工作流程可以基于发送方从未拥有过的数据开始运行。它只能访问您允许的内容:请在“函数”选项卡下的**“商店数据访问**”中授予所需的作用域。默认情况下不会授予任何权限,且会拒绝所有变异操作。

Look up a variant by SKUjavascript
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 }
}

亲自接听来电

ctx.respond() 会立即返回响应,这使得 Webhook 变成了一个小型 API 端点:验证某些内容、计算结果,并在单次请求中完成响应。Shopify Flow 仍会根据您返回的内容继续运行。

Reply to the caller and start a workflowjavascript
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
}

测试

“函数”选项卡上的“测试”面板会使用示例负载运行编辑器中的代码(包括未保存的更改)。它会显示返回值、函数将发送的响应、生成的 Flow 字段、日志行以及执行时长。 此过程不会触发 Shopify Flow,不会写入历史记录,也不会占用您的套餐配额。不过,存储调用是真实的:测试运行会像生产请求一样读写同一个存储。

当函数出错时

如果您的代码抛出异常、超时或返回无法使用的结果,系统将以422状态拒绝该请求,并将失败信息(包括错误详情和您的日志条目)记录在**“历史记录**”中,以便您了解发生了什么。修复代码后重放该条目:重放时将运行新版本的代码。

极限

  • 每次运行耗时5秒,占用64MB内存,代码长度为50,000个字符。
  • 没有 npm 包。ctx.fetch 是唯一的解决方法,且内部或私有地址会被拒绝。
  • ctx.shopify 每次运行允许进行 10 次调用,仅限查询操作,且仅限于您授予的范围。
  • 每次运行到达 Shopify Flow 时,都算作一次正常调用,并计入您的套餐额度。被跳过的请求则不计入。