变换函数
映射功能会从请求中提取值。而转换函数的功能更进一步:它是您自定义的 JavaScript 代码,会在每次请求中于 Shopify Flow 之前执行,因此您可以重构请求负载、进行数据查询、判断该请求是否值得触发工作流程,或者直接向调用方返回响应。
它位于 Webhook 的**“函数**”选项卡中。如果编辑器为空,则表示已关闭。
函数的形状
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) |
立即接听来电 |
重构负载
最常见的用途:将发送方的结构转换为工作流程所需的少量值。
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 },因此不会进行重试。
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 }) |
各页的关键名称和尺寸 |
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 查询,因此工作流程可以基于发送方从未拥有过的数据开始运行。它只能访问您允许的内容:请在“函数”选项卡下的**“商店数据访问**”中授予所需的作用域。默认情况下不会授予任何权限,且会拒绝所有变异操作。
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 仍会根据您返回的内容继续运行。
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 时,都算作一次正常调用,并计入您的套餐额度。被跳过的请求则不计入。

