変換関数
マッピングはリクエストから値を取り出します。一方、トランスフォーム関数はさらに一歩進んだ機能です。これはユーザー自身が記述したJavaScriptであり、Shopify Flowが実行される前のすべてのリクエストに対して実行されます。そのため、ペイロードの形式を変更したり、何かを検索したり、そのリクエストに対してワークフローを実行する価値があるかどうかを判断したり、あるいは呼び出し元に対して直接応答したりすることが可能です。
これは、Webhookの「**Function」**タブにあります。エディタが空の場合は、無効となっています。
関数の形状
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 は実行されず、履歴への記録も行われず、プランの消費も発生しません。呼び出し元には、{ "skipped": true } を含む 200 が返されるため、再試行は行われません。
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クエリを実行するため、ワークフローは送信者がこれまでアクセスできなかったデータを用いて開始することができます。送信者には、許可された情報のみが表示されます。「Function」タブの「Storeデータへのアクセス」で、必要なスコープを許可してください。デフォルトでは何も許可されておらず、ミューテーションは拒否されます。
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エンドポイントとして機能するようになります。つまり、何かを検証し、結果を算出し、1回のリクエストで応答を返すのです。返された内容にかかわらず、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
}テスト
[Function] タブの [Test] パネルでは、エディタ内のコード(保存されていない変更も含む)をサンプルペイロードに対して実行します。ここでは、戻り値、関数が送信するレスポンス、生成される Flow フィールド、ログ行、および実行時間が表示されます。 Shopify Flowは起動されず、履歴にも記録されず、プランの使用量にもカウントされません。ただし、ストレージへの呼び出しは実際に行われます。テスト実行では、本番のリクエストと同様に、同じストアからの読み取りおよび書き込みが行われます。
関数が失敗した場合
コードで例外が発生したり、タイムアウトしたり、使用できない結果が返されたりした場合、そのリクエストは「422」として拒否され、その失敗はエラー内容とログ行とともに「History」に記録されます。これにより、何が起きたかを確認できます。コードを修正し、そのエントリを再生してください。再生時には新しいバージョンが実行されます。
制限
- 1回の実行につき5秒、64MB、そして50,000文字のコードです。
- npmパッケージは使用できません。
ctx.fetchが唯一の解決策ですが、内部アドレスやプライベートアドレスは拒否されます。 ctx.shopify1回の実行につき10回の呼び出しが可能で、クエリのみを対象とし、付与されたスコープに限定されます。- Shopify Flowに到達するすべての実行は通常のリクエストとして扱われ、ご利用のプランのカウント対象となります。スキップされたリクエストはカウント対象外となります。

