變換函數

「映射」會從請求中擷取值。而「轉換函式」的功能則更進一步:它是您自行撰寫的 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 查詢,因此工作流程可以基於發送者從未擁有的資料開始運作。發送者僅能看到您允許的內容:請在「Function」分頁的**「Store 資料存取」**下授予您所需的權限範圍。預設情況下不會授予任何權限,且會拒絕所有變異操作。

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
}

測試

「函式」分頁中的「測試」面板會將編輯器中的程式碼(包含未儲存的變更)套用至範例載荷進行執行。它會顯示回傳值、您的函式會傳送的回應、其產生的Shopify Flow欄位、您的日誌記錄以及執行時間。 此過程不會觸發「Shopify Flow」、不會寫入「歷史紀錄」,也不會計入您的方案配額。不過儲存庫呼叫是真實的:測試執行時會像實際請求一樣讀取和寫入相同的儲存庫。

當函式執行失敗時

如果您的程式碼發生拋出異常、超時或傳回無法使用的結果,系統會以「422」狀態拒絕該請求,並將失敗記錄在**「歷史紀錄」**中,包含錯誤訊息及您的日誌內容,以便您了解發生了什麼情況。修正程式碼後,重新執行該記錄:重新執行時將運行新版本的程式碼。

限制

  • 每次執行需時 5 秒,佔用 64MB 空間,且程式碼長達 50,000 個字元。
  • 沒有 npm 套件。ctx.fetch 是唯一的解決方案,且系統會拒絕內部或私人位址。
  • ctx.shopify 每次執行允許進行 10 次呼叫,僅限查詢,且僅限於您已授予的權限範圍內。
  • 每次執行並成功呼叫 Shopify Flow 的操作,均視為正常呼叫,並計入您的方案配額;被跳過的請求則不計入。