Shopify Scriptsサポート終了:6月30日までにFunctionsへ移行を

Shopify Scriptsサポート終了:6月30日までにFunctionsへ移行を

Shopify Scriptsサポート終了:6月30日までにFunctionsへ移行を

Shopify ScriptsをFunctionsに移行する方法:完全コードチュートリアル(2026年版)— Revizeブログ記事ヘッダー

2026年4月16日。昨日(4月15日)、ShopifyはScript Editorを完全にロックした。もはや新しいScriptの作成や公開はできない。実行停止は75日後の2026年6月30日に迫っている。

もしあなたが、この移行を過去12ヶ月間「次のスプリント」に先送りし続けてきたShopify Plus開発者や、Plusストアを運営するパートナー代理店なら、問題が発生している。それも「後で直せばいい」というレベルではない。「7月1日の午前0時にチェックアウトが機能しなくなる」という問題だ。ほとんどのPlusストアは、何年もかけて5〜20個のScriptを蓄積しており、それらは誰が書いたかも忘れ去られたまま、バックグラウンドで割引ルール、配送方法の非表示、あるいは決済ゲートウェイの制御を静かに処理している。

このガイドは、私たちが今年1月に存在していてほしかったと感じた技術移行マニュアルである。 戦略論だけでなく、実際のコードに踏み込んで解説する。読み終える頃には、Shopify CLIを使用したFunctionの雛形作成から、割引、配送カスタマイズ、決済カスタマイズのためのRustまたはJavaScriptによるロジック記述、タグ付けされた特定の顧客グループに対する安全なテスト、そして既存のチェックアウトを破損させることなくプロダクション環境へデプロイする方法までを理解できるようになる。

さあ、ストアからScriptsの一掃を始め、代わりにFunctionsを設定しよう。



Developer migrating Shopify Scripts to Shopify Functions modules

クイック確認:ScriptsからFunctionsへの移行(60秒要約)

1段落での要約: Shopify Scripts(Script Editor内のRubyコード、Plus限定)は、Shopify Functions(RustまたはJavaScriptで記述され、すべてのプランで利用可能なWebAssemblyモジュール)に置き換わる。shopify app generate extensionを使用してFunctionの基本構造を生成し、必要なカートデータを取得するrun.graphqlクエリを作成、さらに処理結果(割引、配送方法の非表示など)を返すrun.rsまたはrun.jsファイルを記述する。その後、shopify app deployでデプロイし、管理画面またはGraphQLミューテーションを通じて有効化する。FunctionsはコンパイルされたWASMとして5ms未満の低レイテンシで実行され、すべてのプランに対応する。これがShopifyが今後サポートする唯一のカスタマイズパスである。

6月30日に実際に起こること

コード作成を始める前に、スケジュールを確実に把握する必要がある。重要な日付は2つある。


日付

発生する事象

必要な対応

2026年4月15日 (超過)

Script Editorが閲覧専用に。新しいScriptの作成、既存Scriptの編集は不可。

既存のScriptは稼働を維持。移行を開始するか、既存ロジックを確定させる必要がある。

2026年6月30日

すべてのShopify Scriptsが実行を停止する。

この日より前に、代替となるFunctionを本番環境で稼働させる必要がある。

この移行の結果は二者択一である。6月30日までにFunctionがデプロイされ、チェックアウトが正常に機能し続けるか、あるいはデプロイされず、影響を受けるすべてのカートで標準価格、標準配送レート、かつ有効なすべての決済方法にエラーなくデフォルト状態で戻るかのどちらかだ。妥協案は存在しない。Scriptが実行されるか、されないかであり、6月30日以降は完全に実行されなくなる。

ヒント: Shopify管理画面から「設定 → チェックアウト → カスタマイズレポート」を開いてほしい。ここには、ストアで実行中のすべてのScript、その機能、および推奨される代替Functionのタイプがリストアップされている。まずはここから始めよう。

FunctionsとScriptsの違い




項目

Shopify Scripts (廃止予定)

Shopify Functions (代替機能)

開発言語

Ruby DSL (Shopify独自の仕様)

Rust, JavaScript, TypeScript

実行環境

Shopifyインフラ上のサンドボックス環境(Ruby)

WebAssembly (WASM) — 5ms未満の超高速実行

対象プラン

Plusのみ

すべてのプラン(カスタムアプリによる実装はPlus、公開アプリは全プラン開放)

エディタ

管理画面内のScript Editor

ローカルIDE + Shopify CLI

バージョン管理

なし — 直接編集

Git対応 — 完全なバージョン履歴管理

テスト方法

チェックアウト画面での手動検証

shopify app devによるローカル環境テスト、プレビューリンク検証

デプロイ

管理画面上で「保存」を実行

ターミナルからshopify app deployを実行

制御対象

ラインアイテム、配送、決済

割引、Cart Transform、バリデーション、配送カスタマイズ、決済カスタマイズ、注文ルーティング、フルフィルメント制約など多数

設計思想の変化は重大である。Scriptsは「テキストエリア内でRubyをいじる」ものだったが、Functionsは「本格的なアプリを書く。バージョン管理を行い、ローカルでテストし、CIパイプラインでデプロイする」というプロセスになる。学習曲線はより急だが、チェックアウトロジックとしての移行はこれで最後になる見通しだ。Functionsは、Scriptsのような一時しのぎの対応ではなく、Shopifyが長期にわたってコミットする標準仕様である。

お使いのScriptsと対応するFunctionタイプのマップ

現在お使いのすべてのScriptは、正確にひとつのFunction APIへとマッピングされる。以下が参照すべき移行一覧である。


従来のScriptタイプ

従来の動作内容

移行先のFunction API

ターゲット

Line Item Script

特定の商品、顧客、またはカートの条件に応じた割引適用

Cart & Checkout Discounts API

cart.lines.discounts.generate.run

Shipping Script(割引)

カート内の条件に基づく無料または割引配送の提供

Cart & Checkout Discounts API

cart.delivery-options.discounts.generate.run

Shipping Script(非表示 / リネーム / 並び替え)

設定値以上のカートに対する配送料の非表示や、「通常便」を「50ドル以上で送料無料」へ変更

Delivery Customization API

cart.delivery-options.transform.run

Payment Script

B2B向けのPayPal非表示、一定額以上の代金引換の非表示、表示順の並び替え

Payment Customization API

cart.payment-methods.transform.run

カート内容変更Script(稀)

商品セット(バンドル)の作成、ラインアイテムの置換

Cart Transform API

cart.transform.run

チェックアウト拒否Script

無効なSKU組み合わせ時にチェックアウト画面をブロック

Cart & Checkout Validation API

cart.validations.generate.run

もし10個のScriptを運用している場合、それらは3〜5個のFunctionに統合できる可能性が高い。複数の記述をひとつのFunction内の洗練された条件分岐ロジックへ整理できるためである。



Shopify Functions unifying discounts, delivery, and payment customizations

前提条件: ローカル開発環境のセットアップ

Functionの構築を開始する前に、以下の3つの環境をローカル端末にインストールし、ターミナルで確認を行う必要がある。

1. Node.js 18+


node --version
# 18.0.0 以上必須
node --version
# 18.0.0 以上必須

バージョンが古い場合は、nvmを介してインストールするか、nodejs.orgからダウンロードすること。

2. Shopify CLI 3+


npm install -g @shopify/cli@latest
shopify version
# 3.x 以上の出力を確認
npm install -g @shopify/cli@latest
shopify version
# 3.x 以上の出力を確認

3. Rust ツールチェーン (RustでFunctionを開発する場合のみ必要)


curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version

JavaScriptでFunctionを作成する場合、Rustは不要。開発チーム内でいずれかの言語を選択し、統一して管理することを推奨する。混在させると、メンテナンスコストが増大する。

4. 開発用ストア

Shopifyパートナーダッシュボードから新規開発用ストアを作成するか、既存の開発用ストアを利用する。本番環境に適用する前に、まずここで検証を行う。

最初のFunctionを生成する

基本的な構成ファイルの生成はCLIに任せることができる。任意の作業ディレクトリで以下を実行する。


# 新規Shopifyアプリの初期化 (既存のアプリがない場合のみ実行)
shopify app init my-checkout-functions
cd my-checkout-functions

# Function拡張機能の生成
shopify app generate extension
# 新規Shopifyアプリの初期化 (既存のアプリがない場合のみ実行)
shopify app init my-checkout-functions
cd my-checkout-functions

# Function拡張機能の生成
shopify app generate extension

CLIプロンプトの手順に従って設定を入力する。割引Functionを作成する場合は、以下のオプションを選択する。

  • タイプ: Function

  • テンプレート: discount (あるいは cart_checkout_validation, delivery_customization, payment_customization など選択可能)

  • 言語: Rust または JavaScript

  • 名称: 任意の名前(例:volume-discount-fn

この操作により、extensions/volume-discount-fn/ ディレクトリ配下に、以下の構成ファイルが生成される。


extensions/volume-discount-fn/
├── shopify.extension.toml      # Functionの設定(ターゲット、ビルド、バージョン等)
├── src/
├── cart_lines_discounts_generate_run.graphql   # インプット用クエリ
└── cart_lines_discounts_generate_run.rs        # Functionの処理ロジック
├── Cargo.toml                  # Rustの依存関係管理(Rust開発時のみ)
└── README.md
extensions/volume-discount-fn/
├── shopify.extension.toml      # Functionの設定(ターゲット、ビルド、バージョン等)
├── src/
├── cart_lines_discounts_generate_run.graphql   # インプット用クエリ
└── cart_lines_discounts_generate_run.rs        # Functionの処理ロジック
├── Cargo.toml                  # Rustの依存関係管理(Rust開発時のみ)
└── README.md

以降、開発中に編集を繰り返すことになるファイルは .toml(設定)、.graphql(インプット)、そして .rs または .js(処理ロジック)の3点のみである。

チュートリアル 1: Line Item Script の置き換え (ボリューム割引)

例として、特定のコレクションに属する商品が「カート内に5点以上」追加された際、小計から10%割引を適用する従来のScriptを、Functionとして置き換える手順を示す。

ステップ 1.1: 設定ファイルの記述 (shopify.extension.toml)


api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]
api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]

Step 1.2: インプット用 GraphQL クエリ (src/cart_lines_discounts_generate_run.graphql)


query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}
query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}

ヒント: Functionは、GraphQLでクエリを定義したデータのみを認識する。不要なフィールドを除外してクエリデータを最小限に維持することが、実行速度向上とリソース節約に繋がる。

ステップ 1.3: ロジック記述 (src/cart_lines_discounts_generate_run.rs)


use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // 割引区分がマッチしない場合は処理を中断
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 割引対象のコレクション内商品の合計数量を算出
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 注文小計から10%引きを適用
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}
use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // 割引区分がマッチしない場合は処理を中断
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 割引対象のコレクション内商品の合計数量を算出
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 注文小計から10%引きを適用
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}

ステップ 1.4: テスト、デプロイ、有効化


# ホットリロード対応のローカルテスト起動
shopify app dev

# 準備完了後、本番へデプロイ
shopify app deploy

# デプロイ後に表示されるGraphiQLコンソール (開発ターミナルで `g` キーを押下する) にて、
# 作成したFunctionを利用する自動割引を作成する:
# ホットリロード対応のローカルテスト起動
shopify app dev

# 準備完了後、本番へデプロイ
shopify app deploy

# デプロイ後に表示されるGraphiQLコンソール (開発ターミナルで `g` キーを押下する) にて、
# 作成したFunctionを利用する自動割引を作成する:
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}

これで、新開発されたFunctionがデプロイ・バージョン管理され、旧Scriptからの置き換えが完了する。

チュートリアル 2: Shipping Script の置き換え (小計に応じた特定配送方法の制限)

よくあるScriptの例として、「大型品の特急配送コスト高騰を防ぐため、カートの注文小計が500ドルを超える場合は特急配送(Express)を表示候補から除外する」というケースについて、Delivery Customization APIによる実装例を以下に示す。

ステップ 2.1: 雛形(拡張機能)の生成


shopify app generate extension --template delivery_customization --name hide-express-fn
shopify app generate extension --template delivery_customization --name hide-express-fn

ステップ 2.2: インプット用 GraphQL クエリ (src/run.graphql)


query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}
query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}

ステップ 2.3: 処理ロジック (src/run.js — JavaScriptでの実装例)


// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}
// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}

ステップ 2.4: 管理画面を介した機能有効化 (GraphQLによる実行は不要)

配送のカスタマイズ(Delivery Customizations)には、Shopify標準の管理画面UIが用意されている。shopify app deploy実行後に以下の手順で設定を行う。

  1. 管理画面から 設定 → 配送と配達 へ移動する

  2. 最下部付近にある カスタマイズ セクションスクロールする

  3. カスタマイズを追加 ボタンをクリックし、デプロイ済みの該当Functionを選択する

  4. 変更内容を保存する

これにより、本番環境で配送の非表示ロジックの有効化が完了する。



Shopify delivery customization Function hiding shipping option at checkout

チュートリアル 3: Payment Script の置き換え (B2Bユーザーの「代金引換」制限)

従来のScriptの例として、「『B2B』タグを保持する顧客に対してチェックアウト画面から『代金引換』支払を選択肢として非表示にする」処理の、Payment Customization Function版の構築例を示す。

ステップ 3.1: 雛形(拡張機能)の生成


shopify app generate extension --template payment_customization --name hide-cod-b2b-fn
shopify app generate extension --template payment_customization --name hide-cod-b2b-fn

ステップ 3.2: インプット用 GraphQL クエリ


query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}
query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}

ステップ 3.3: 処理ロジック (src/run.js)



const NO_CHANGES = { operations: [] }; export function run(input) { const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0]; const isB2B = tagCheck?.hasTag === true; if (!isB2B) return NO_CHANGES; const codMethod = input.paymentMethods.find((pm) => pm.name.toLowerCase().includes("cash on delivery") ); if (!codMethod) return NO_CHANGES; return { operations: [{ hide: { paymentMethodId: codMethod.id } }], }; }

ステップ 3.4: 有効化の手順

決済のカスタマイズ(Payment Customizations)も同様に、管理画面の 設定 → 決済 → カスタマイズ セクションから設定用のUIが提供されている。配送設定の手順と同様に、該当のFunctionを選択して保存するのみで適用が完了する。

余談:購入完了後のフロー処理について

ここで、このブログの著者であるRevizeより1つ共有がある。我々が取り扱う領域は、FunctionAPIでは届かない「決済処理が完了した後の状態」である。注文が通った後に「商品を追加したい」「サイズ変更が必要になった」「配送先指定を直したい」「割引を適用し忘れた」といった顧客要望が発生することがある。Functionsはチェックアウト時のガードレールとして、Revizeは主に決済完了後の管理に位置づけられる。 Functionsはカートに追加可能な商品の是非や構成のルールを決め、Revizeは返金と再購入という不要な二度手間を踏ませることなく、顧客自身やサポートチームがシームレスに変更操作を実行できるように支援する。これは、手動対応では運用不可能なボリュームとなる大手のPlus事業者や高頻度取引ストア、あるいは優れた顧客体験を重視する事業者にとって、購入リピート率を守るために極めて不可欠な要素となり得る。

もしScriptsからFunctionsへの移行準備が現在進んでいても、上記のような購入後の追跡・変更の仕組みを整備できていなければ、数週間後に別のフローボトルネックに直面することになるだろう。我々が直近公開した注文管理設計ガイドには、ポストチェックアウト時における最良のシナリオを詳述している。

それでは、移行プロセスの解説に戻る。

安全に切り替える:テスト用タグパターンによる検証

現在のShopify Functionsには、管理画面から簡単に「下書き保存」やワンボタンでの切り替えのみが可能な「ドラフト実行モード」が備わっていない。推奨される標準的なテストパターンとして、新しいFunctionのロジック実行を「テスト用の顧客タグ」に紐づけて限定適用し、既存のScriptと同時に並行稼働させ、特定ユーザーに不具合がないことを実データで確認してから、全体への完全な切り替えに移行するというフローを踏むのが健全である。

手順 1: テスト用のタグを任意の顧客に割り振る

管理画面の 顧客管理 セクションから、自社内の確認用アカウントに FN-TESTER などの検証用タグを追加する。

手順 2: コード上で「タグ存在による分岐」を追加する


// run ファンクション内の開始部分にて判定を記述
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // タグがない場合は、既存のScript(判定処理)にフォールバックさせる
}

// 新しいテスト仕様となるFunction側の処理を以下へ続行記述
// run ファンクション内の開始部分にて判定を記述
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // タグがない場合は、既存のScript(判定処理)にフォールバックさせる
}

// 新しいテスト仕様となるFunction側の処理を以下へ続行記述

手順 3: インプット用クエリに hasAnyTag の取得を追加する


cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}
cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}

手順 4: 本番チェックアウト環境にて実地検証

タグの設定された確認用顧客アカウントでログインして実際に仕様を確認し、新規のFunctionによる分岐が正確に稼働することを確認する。一方でタグがない別の一般アカウントで検証し、既存の旧Script側の処理が適切に動くことを担保する。双方に不整合が発生せず数日間正常に動作することを確認できれば、分岐ロジック部分の記述(タグチェック)を削除し、全員に向けて完全適用する。

手順 5: 既存の古いScriptを無効化する

管理画面から アプリ管理 → Script Editor → [対象のScript] → 非公開(Unpublish) を実行する。非公開化されると、新規導入したFunction側が唯一の設定指標となる。

複数人で運用可能なスケーラブルなデプロイワークフロー

開発者のローカルPCなどから常にデプロイを手続きし続けるべきではない。最初のいくつか動作に必要な構築が完了した後は、継続的な自動統合(CI)パイプラインへとプロセスを移行させる。

構成可能なCIの例、最小構成ファイル


# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}
# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}

パートナーダッシュボードの 設定 → トークン 項目から必要な生成用トークン(Partner Token)を登録する。これにより、main ブランチへのマージを契機に、Functionsが安全に自動生成・適用される体制が構築できる。

バージョン管理とロールバック

shopify app deploy コマンドを実行するごとに、自動的に固有のスナップショットバージョンが作成・保持される。もしエラーが発生した際には、以下のコマンドにより容易に以前の履歴にロールバックできる。


shopify app versions list
shopify app release --version <前のバージョンID

shopify app versions list
shopify app release --version <前のバージョンID

Scriptsにおいて、旧コードを記憶またはバックアップから抽出し、管理画面に貼り付け直して戻していた工程と比べれば、圧倒的に整合性の取れた運用管理が可能となる。



Shopify Functions deployment pipeline across local staging and production environments

移行時に陥りがちなよくある失敗事例

我々がこれまで1年間にわたりPlus導入店舗へ伴走してきた実績から、よく見られる典型的な5つのミスタイプを以下に挙げる。

1. 既存のScriptと1対1でそっくり移植しようと試みること。 これは間違いである。記述構造をよく整理すれば、ひとつの優秀なFunctionのみに役割を統合できる余地がある。実装前にすべてのロジックパターンを一度見直すことが推奨される。

2. APIの読み書き用アクセス権限(アクセススコープ)の追加を忘れること。 多くのFunctionsは動作時に read_customers, read_orders, write_discounts などの設定スコープを必要とする。shopify.app.toml ファイル内に記述を追加した上で、アプリの管理者ページから再認証権限許可を実施していない場合、インプット用クエリが期待通りに動かず null を返すことになる。

3. 段階的なテストを行わず本番ユーザー全員へ一括反映させてしまうこと。 コードが一見完璧に書けているように見えても、実環境のイレギュラーな条件(空のカート、ギフト券決済併用時、注文エラー時の振る舞い、B2Bの下書き注文等)により予想外の事態が容易に発生する。テスト用タグによる段階的リリースを数日間挟むだけで、重大な障害事故のリスクをゼロに抑えることができる。

4. 「カスタマイズレポート」の一覧未確認。 本レポートは現在稼働しているScriptsの実態を網羅データとして最も的確に示すドキュメントである。開発チームの不確実な記憶からではなく、レポート上の明確な結果を手掛かりに進めることを徹底してほしい。

5. コレクションIDや顧客タグの値をコード中に直接ハードコードして直接書き込んでしまうこと。 マーチャント担当者側で制御を可変にするため、メタフィールドを活用した設定UIを挟み込むことが可能である。CLIを介してメタフィールドとの連携による制御構造を容易に構築できるため、詳細はShopifyの公式ドキュメント(Function Configuration)に目を通してほしい。

サービス完全移行までのガント検証スケジュール(各週ロードマップ)

残り75日を無事故で完全にクリアするためのモデル計画表を公開する。


週数

対応アクション

第1週 (今週から)

「カスタマイズレポート」を取得、解析の上、全Scriptsをリスト化。個々に独自のFunctionに移植するか、アプリに切り替えるか、廃止削減するか決定する。

第2~3週目

ローカル開発環境一連のセットアップ。最初の雛形のFunctionを作成し、最も仕様の単純な決済方法制御の非表示仕様を実装してみる。

第4~6週目

割引仕様の移植に着手。割引APIは考慮項目・取得範囲が最も多岐にわたる。並行的にテスト仕様によるチェックアウト検証を継続する。

第7~8週目

配送オプション、配送料制御の移植完了。管理UI上にて各テスト適用の設定を実施する。

第9~10週目

CI自動化デプロイパイプラインを作成。ローカル接続でのマニュアル管理から完全に脱却する。

第11週目

最後の総合稼働整合チェック。すべてのScriptの非公開化。約2週間Functionsの安定稼働状態を確認する。

6月30日当週

旧スクリプト廃止当日。事前に全対応を終えているためリスクなく安全に完了。

今週すぐに手を付け始めれば、十分なリカバリー期間を確保できる。ギリギリになってからでは間に合わない。



Week-by-week migration roadmap from Shopify Scripts to Functions

よくある質問(FAQ)

Functionsを利用するにはShopify Plus契約が必須か?

完全オリジナルの自社専用Functionを作成・適用する場合はShopify Plus契約が必要であるが、すでに他のベンダーから提供されApp Store上に公開されているアプリ内のFunctionsを利用する場合は、全プラン(ベーシック、スタンダード等)でそのまま利用可能である。 もしPlusを利用していない事業者であれば、要件を満たせる既存公開アプリから導入を行うか、自社独自のFunctionの開発が必要な場合はPlusへプランをアップグレードする必要がある。ただし、元々Scriptsを運用していたような大規模マーチャントであれば基本的にPlus環境で運用しているケースが大半を占めるため、本仕様による影響は軽微であるといえる。

TypeScriptを用いて記述することは可能か?

可能。TypeScriptは完全対応しており、CLI経由でプロジェクト設定ファイル等を一括生成できる。 コマンド shopify app generate extension を走らせ、出力先の選択として「JavaScript」を指定することで、コンパイルに必要な型定義ファイル import("../generated/api") がディレクトリ内に含まれる。必要に応じてファイルを .ts 拡張子へ書き換え、tsconfig.json などを構築に追記可能。ビルド後に生成されるWASMモジュールの仕様はどれを選択しても不変である。

処理性能(実行速度)はScriptsと比較してどの程度差があるか?

通常5ms以下。Rubyで動作していた従来のScript実行性能と比較すると、劇的に高速化されている。 FunctionsはWebAssemblyとして稼働する最適化されたコンパイルモジュールであるため、Shopify上での実行要件として「1動作5ms以下」を原則として規定している。もしこの制限時間を超えた場合は処理がエラーとなってスキップされ、何も結果を出力しない。実際、適切にビルドされたコードは1〜2ms程度で高速処理をパスできる構造になっている。実行時のパフォーマンスポテンシャルは旧来の比ではない。

Function内部から外部のAPIサーバー等へ通信してデータを取得できるか?

不可。Functions内部での外部ネットワーク要求は設計上限。 Functionsは純粋なコンピューティング処理(インプットとなるカート入力を元に、決められたフォーマットで瞬時にアウトプットオペレーションを返すこと)に役割を特化している。仮に外部連携データ(外部CRM情報のルックアップ、外部在庫状況のクエリ情報)が必要な場合、該当データを事前にメタフィールド上に顧客等に予め書き出して格納しておくか、あるいはアプリプロキシ(App Proxy)、Webhook、購入前Cart Transform APIのバックエンドサーバー処理などの別の解決策を選択する必要がある。移行検討プロセスにおいて、システム全体の再設計が必要とされる一番大きな理由がこの点にある。

Cart Transform API と Discounts API の切り分け目安は?

価格変動の算出(ディスカウント値の計算・適用)はDiscounts APIが担い、カート内の構成や商品そのものの切り替え・操作はCart Transform APIが担当する。 カート内の行数変更を行わずに単純な割引額のみを調整する場合は前者。2つの異なる商品のセット購入を契機に単一のセット商品(構成)へと自動的に置き換えを施す、あるいは単一のアイテム指定をバラ売り状態へ内部的にマッピング分解する等の制御が必要な場合は後者を利用する。古いScriptsではこれらを同じ場所で同時に処理させているケースがあったため、新しい設計では適切な役割へとプログラムを切り分ける必要がある。

別途開発ストアを新規に用意せずに、ローカル環境だけで完全な検証を行えるか?

単体でのユニットテストや、CLIでサンプルインプットを流し込んでの動作検証は cargo testnpm test あるいは shopify app function run コマンドだけで実施可能である。しかし、結合テストや実環境での仕様検証には、オンラインの開発ストアへの接続が必ず不可欠となる。 最終的な支払処理やUI表示変更などのチェックアウトでの実機を伴う動作の裏付けを行うには、実際に稼働する画面に触れる環境を用意することが必要となる。

同一仕様(ターゲット)の複数のFunctionを同時稼働させることは可能か?

可能。同ターゲット設定に対して機能を追加した場合、それらはあらかじめ規定された順序で実行される。 割引処理に関して、複数のFunctionが設定されているケースでは、Shopify標準の併用(自動適用・割引スタック順等の定義)ルールに従って処理され適用される。決済や配送カスタマイズ等の処理については順を追ってデータがパイプされパイプラインに受け渡される。ただ、要件や管理を極力シンプルにまとめるため、基本設計としては対象ターゲット毎に集約して定義することを推奨する。

新しいFunctionをデプロイした後、古い既存のScript側はどう稼働に影響するか?

Script Editor側の管理画面から明示的に非公開にするまで、双方の仕様が並行して同時にチェックアウト上に実行される。 動作の不整合・不具合を避けるために適切なチェック期間、検証設定を用意して対応する必要があるため、テストタグを設置して動作検証する手法が最も推奨される。並行稼働テストが無事終了すれば、旧Scripts側を管理画面から速やかに非公開化すること。2026年6月30日以降はこれらの設定状態によらず、Scripts自体が例外なくすべて完全に稼働停止する。

今回の切り替えにより、既存ECサイトのSEO評価やテーマ等のデザイン崩れに悪影響はあるか?

一切影響しない。Functionsはサーバーサイドのみで即座に計算され動作するため、顧客の前で構築されているフロントエンドテーマの表示要素や、商品ページのマークアップ情報、構造化記述データを含め一切干渉しない。 カスタマイズの影響範囲はあくまでチェックアウト画面内、支払方法の決定、配送料情報の反映等の部分に制限される。

カートラインオブジェクトに設定されたカスタムプロパティ(カスタム属性データ)にアクセスしていたScriptはどう移行すべきか?

GraphQLクエリ内からカート内の対象オブジェクトに対して attribute フィールドを追加・定義することで容易に取得できる。 チェックアウトのライン情報配下に attribute(key: "your-key") { value } をクエリに差し込むことで、従来のRubyで実行していた引数メソッド等の手順をそのまま置き換える形で情報を拾うことが可能となる。

処理結果情報に基づく、アナリティクス計測や、注文完了後に注文内容に対してタグを付与する等の書き込み作業を直接Functionから行えるか?

不可能。Functions自体は「カートの内容に対して出力するアクション情報を返すのみ」の完全に純粋で独立したモジュールであり、自身の内部からタグ等の情報を書き換えるためのデータ発行やWebhook等の直接イベントを起動させることはできない。 タグの変更対応等のバックヤード的な調整が稼働に必要な場合、注文完了時のシステム動作イベントをきっかけとして別の仕掛け(Shopify Flow)などでタグのコントロールや更新情報の送出などをカバーすることを推奨する。

自作アプリを一から開発せずとも要件を満たせる、オープンな既存の代替公開アプリはストア上に提供されているか?

豊富に用意されている。Shopifyアプリストアには優秀な対応ツール・システムが非常に多くすでに並んでいる。 「割引 Function」「配送 追加 カスタマイズ」「決済カスタマイズ」等の類似キーでストアを検索してほしい。ボリューム割引、タグ情報に基づく決済制限等の一般的なユースケースであれば、十分にそれらの外部パッケージを使い回すだけで追加の開発期間とコストを大幅に削減できる。完全に独自の複雑な内製ポリシーでない限り、そのような既存仕様の適合案を十分に優先検討することを推奨する。

仮に移行が間に合わず、6月30日の期限を過ぎてシステムを放置した場合に起こるリスクは?

稼働していた仕組みは有無を言わさずその瞬間から作動しなくなる。延命処理や、救済猶予期間は一切存在しない。 割引、制限を掛けていた特殊な配送料情報、非表示処理を加えていた支払方法等の設定は、UTC日付で7月1日0時をもってすべて機能不全に陥り、管理されていないありのままの条件(一般決済、標準配送)で即座に決済に回ることになる。売上額等への実被害や注文トラフィック上の致命的な損失が生じる可能性が極めて高く、このリスク要因は何よりも致命的になりかねない。移行プロセス全体の工程ボリュームと、想定より工数が掛かることを考慮して、期限に対して余裕のある計画を進めるのが必須である。

移行が完全に正常完了した時点で、旧アプリ「Script Editor」を管理画面から直ちに削除して問題ないか?

削除して全く問題ない。移行を完結してFunctionsのみで運営する状態が完全にできていれば、アプリは不要になる。不都合がなければ当セクションが完全無効化・廃除される期限前に直ちにアンインストール手続きを行ってよい。

今週から着手すべきアクション

この詳細記事をただ単に読み終わって、作業用PCのブラウザを闭じて満足することのないように進めてほしい。即行動できる事項として、以下の4つの課題は一歩として最初の一週間以内に完了させよう。

1. 稼働状況をレポートで完全に可視化する。 ECストアの管理画面内の「設定 → チェックアウト → カスタマイズレポート」にアクセスし、データをダウンロード保存すること。これがそのままあなたの行うべき移行工程の優先リストとなる。

2. ローカルの開発用のプログラム整備。 Node.js 18+ の設置、Shopify CLI、Rustの開発用設定手順を確認のうえ、shopify version がエラーを出さずに動作することを検証する。15〜30分もあれば全て完了する。

3. 仮のFunction(モジュール)を開発ストア上で試しにデプロイしてみる。 最も構成の理解が容易な、決済方法の限定ルール等をダミーとして一から動作検証ベースで完結させてみるだけで構わない。実際に本番稼働へと適用しなくても、そのプロセスを通せる一連のツールチェーン動作が保証されれば多大なる進捗と信頼感に繋がる。

4. 今後8週間分、開発スケジュールに対象のタスク用確保枠をカレンダー等に固定して設ける。 優先移行プロセスは定常業務の合間の手隙で行うべき軽微な案件として処理してはならない。社内の定期スプリント、およびリリースサイクルを意識し、工数の優先枠として確立して取り組んでほしい。

実際に計画通りに正常に移行を終わらせたクライアント達を見ていると、対応の型は常に類似を見せている。「事前調査・設計に要した約2週間、実際の機能開発と実装調整におよそ3週間、不具合修正やタグ等を用いたテスト管理検証におよそ1週間」。合計6週間。およそ10週間ある残りのタイミングから鑑みて、テストプロセスや確認作業の品質を最良まで仕上げる為、まだ十分にこのスケジュールに収まるバッファ、機会を活かすことができる。

チェックアウトにおけるカート仕様が綺麗に整った後に、次に多くの大規模チームが目を向ける課題がまさにポストチェックアウト(配送先住所の迅速なケア変更、購入後の別のサイズ、カラー差し替え、注文後の個別プロモーション適用など)である。自サイトにおける最高の顧客購買体験への設計、改善をこの機会に検討される場合は、この全ての新規Functionsと共に滑らかに調和しながら連携動作可能なRevizeがすでにShopifyアプリストアから公開中であるため、ぜひ併せて参考材料として活用してほしい。

関連リファレンス(公式ドキュメント等一覧)


こちらの関連ドキュメント記事もよく参照されています


2026年4月16日。昨日(4月15日)、ShopifyはScript Editorを完全にロックした。もはや新しいScriptの作成や公開はできない。実行停止は75日後の2026年6月30日に迫っている。

もしあなたが、この移行を過去12ヶ月間「次のスプリント」に先送りし続けてきたShopify Plus開発者や、Plusストアを運営するパートナー代理店なら、問題が発生している。それも「後で直せばいい」というレベルではない。「7月1日の午前0時にチェックアウトが機能しなくなる」という問題だ。ほとんどのPlusストアは、何年もかけて5〜20個のScriptを蓄積しており、それらは誰が書いたかも忘れ去られたまま、バックグラウンドで割引ルール、配送方法の非表示、あるいは決済ゲートウェイの制御を静かに処理している。

このガイドは、私たちが今年1月に存在していてほしかったと感じた技術移行マニュアルである。 戦略論だけでなく、実際のコードに踏み込んで解説する。読み終える頃には、Shopify CLIを使用したFunctionの雛形作成から、割引、配送カスタマイズ、決済カスタマイズのためのRustまたはJavaScriptによるロジック記述、タグ付けされた特定の顧客グループに対する安全なテスト、そして既存のチェックアウトを破損させることなくプロダクション環境へデプロイする方法までを理解できるようになる。

さあ、ストアからScriptsの一掃を始め、代わりにFunctionsを設定しよう。



Developer migrating Shopify Scripts to Shopify Functions modules

クイック確認:ScriptsからFunctionsへの移行(60秒要約)

1段落での要約: Shopify Scripts(Script Editor内のRubyコード、Plus限定)は、Shopify Functions(RustまたはJavaScriptで記述され、すべてのプランで利用可能なWebAssemblyモジュール)に置き換わる。shopify app generate extensionを使用してFunctionの基本構造を生成し、必要なカートデータを取得するrun.graphqlクエリを作成、さらに処理結果(割引、配送方法の非表示など)を返すrun.rsまたはrun.jsファイルを記述する。その後、shopify app deployでデプロイし、管理画面またはGraphQLミューテーションを通じて有効化する。FunctionsはコンパイルされたWASMとして5ms未満の低レイテンシで実行され、すべてのプランに対応する。これがShopifyが今後サポートする唯一のカスタマイズパスである。

6月30日に実際に起こること

コード作成を始める前に、スケジュールを確実に把握する必要がある。重要な日付は2つある。


日付

発生する事象

必要な対応

2026年4月15日 (超過)

Script Editorが閲覧専用に。新しいScriptの作成、既存Scriptの編集は不可。

既存のScriptは稼働を維持。移行を開始するか、既存ロジックを確定させる必要がある。

2026年6月30日

すべてのShopify Scriptsが実行を停止する。

この日より前に、代替となるFunctionを本番環境で稼働させる必要がある。

この移行の結果は二者択一である。6月30日までにFunctionがデプロイされ、チェックアウトが正常に機能し続けるか、あるいはデプロイされず、影響を受けるすべてのカートで標準価格、標準配送レート、かつ有効なすべての決済方法にエラーなくデフォルト状態で戻るかのどちらかだ。妥協案は存在しない。Scriptが実行されるか、されないかであり、6月30日以降は完全に実行されなくなる。

ヒント: Shopify管理画面から「設定 → チェックアウト → カスタマイズレポート」を開いてほしい。ここには、ストアで実行中のすべてのScript、その機能、および推奨される代替Functionのタイプがリストアップされている。まずはここから始めよう。

FunctionsとScriptsの違い




項目

Shopify Scripts (廃止予定)

Shopify Functions (代替機能)

開発言語

Ruby DSL (Shopify独自の仕様)

Rust, JavaScript, TypeScript

実行環境

Shopifyインフラ上のサンドボックス環境(Ruby)

WebAssembly (WASM) — 5ms未満の超高速実行

対象プラン

Plusのみ

すべてのプラン(カスタムアプリによる実装はPlus、公開アプリは全プラン開放)

エディタ

管理画面内のScript Editor

ローカルIDE + Shopify CLI

バージョン管理

なし — 直接編集

Git対応 — 完全なバージョン履歴管理

テスト方法

チェックアウト画面での手動検証

shopify app devによるローカル環境テスト、プレビューリンク検証

デプロイ

管理画面上で「保存」を実行

ターミナルからshopify app deployを実行

制御対象

ラインアイテム、配送、決済

割引、Cart Transform、バリデーション、配送カスタマイズ、決済カスタマイズ、注文ルーティング、フルフィルメント制約など多数

設計思想の変化は重大である。Scriptsは「テキストエリア内でRubyをいじる」ものだったが、Functionsは「本格的なアプリを書く。バージョン管理を行い、ローカルでテストし、CIパイプラインでデプロイする」というプロセスになる。学習曲線はより急だが、チェックアウトロジックとしての移行はこれで最後になる見通しだ。Functionsは、Scriptsのような一時しのぎの対応ではなく、Shopifyが長期にわたってコミットする標準仕様である。

お使いのScriptsと対応するFunctionタイプのマップ

現在お使いのすべてのScriptは、正確にひとつのFunction APIへとマッピングされる。以下が参照すべき移行一覧である。


従来のScriptタイプ

従来の動作内容

移行先のFunction API

ターゲット

Line Item Script

特定の商品、顧客、またはカートの条件に応じた割引適用

Cart & Checkout Discounts API

cart.lines.discounts.generate.run

Shipping Script(割引)

カート内の条件に基づく無料または割引配送の提供

Cart & Checkout Discounts API

cart.delivery-options.discounts.generate.run

Shipping Script(非表示 / リネーム / 並び替え)

設定値以上のカートに対する配送料の非表示や、「通常便」を「50ドル以上で送料無料」へ変更

Delivery Customization API

cart.delivery-options.transform.run

Payment Script

B2B向けのPayPal非表示、一定額以上の代金引換の非表示、表示順の並び替え

Payment Customization API

cart.payment-methods.transform.run

カート内容変更Script(稀)

商品セット(バンドル)の作成、ラインアイテムの置換

Cart Transform API

cart.transform.run

チェックアウト拒否Script

無効なSKU組み合わせ時にチェックアウト画面をブロック

Cart & Checkout Validation API

cart.validations.generate.run

もし10個のScriptを運用している場合、それらは3〜5個のFunctionに統合できる可能性が高い。複数の記述をひとつのFunction内の洗練された条件分岐ロジックへ整理できるためである。



Shopify Functions unifying discounts, delivery, and payment customizations

前提条件: ローカル開発環境のセットアップ

Functionの構築を開始する前に、以下の3つの環境をローカル端末にインストールし、ターミナルで確認を行う必要がある。

1. Node.js 18+


node --version
# 18.0.0 以上必須

バージョンが古い場合は、nvmを介してインストールするか、nodejs.orgからダウンロードすること。

2. Shopify CLI 3+


npm install -g @shopify/cli@latest
shopify version
# 3.x 以上の出力を確認

3. Rust ツールチェーン (RustでFunctionを開発する場合のみ必要)


curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version

JavaScriptでFunctionを作成する場合、Rustは不要。開発チーム内でいずれかの言語を選択し、統一して管理することを推奨する。混在させると、メンテナンスコストが増大する。

4. 開発用ストア

Shopifyパートナーダッシュボードから新規開発用ストアを作成するか、既存の開発用ストアを利用する。本番環境に適用する前に、まずここで検証を行う。

最初のFunctionを生成する

基本的な構成ファイルの生成はCLIに任せることができる。任意の作業ディレクトリで以下を実行する。


# 新規Shopifyアプリの初期化 (既存のアプリがない場合のみ実行)
shopify app init my-checkout-functions
cd my-checkout-functions

# Function拡張機能の生成
shopify app generate extension

CLIプロンプトの手順に従って設定を入力する。割引Functionを作成する場合は、以下のオプションを選択する。

  • タイプ: Function

  • テンプレート: discount (あるいは cart_checkout_validation, delivery_customization, payment_customization など選択可能)

  • 言語: Rust または JavaScript

  • 名称: 任意の名前(例:volume-discount-fn

この操作により、extensions/volume-discount-fn/ ディレクトリ配下に、以下の構成ファイルが生成される。


extensions/volume-discount-fn/
├── shopify.extension.toml      # Functionの設定(ターゲット、ビルド、バージョン等)
├── src/
├── cart_lines_discounts_generate_run.graphql   # インプット用クエリ
└── cart_lines_discounts_generate_run.rs        # Functionの処理ロジック
├── Cargo.toml                  # Rustの依存関係管理(Rust開発時のみ)
└── README.md

以降、開発中に編集を繰り返すことになるファイルは .toml(設定)、.graphql(インプット)、そして .rs または .js(処理ロジック)の3点のみである。

チュートリアル 1: Line Item Script の置き換え (ボリューム割引)

例として、特定のコレクションに属する商品が「カート内に5点以上」追加された際、小計から10%割引を適用する従来のScriptを、Functionとして置き換える手順を示す。

ステップ 1.1: 設定ファイルの記述 (shopify.extension.toml)


api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]

Step 1.2: インプット用 GraphQL クエリ (src/cart_lines_discounts_generate_run.graphql)


query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}

ヒント: Functionは、GraphQLでクエリを定義したデータのみを認識する。不要なフィールドを除外してクエリデータを最小限に維持することが、実行速度向上とリソース節約に繋がる。

ステップ 1.3: ロジック記述 (src/cart_lines_discounts_generate_run.rs)


use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // 割引区分がマッチしない場合は処理を中断
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 割引対象のコレクション内商品の合計数量を算出
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 注文小計から10%引きを適用
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}

ステップ 1.4: テスト、デプロイ、有効化


# ホットリロード対応のローカルテスト起動
shopify app dev

# 準備完了後、本番へデプロイ
shopify app deploy

# デプロイ後に表示されるGraphiQLコンソール (開発ターミナルで `g` キーを押下する) にて、
# 作成したFunctionを利用する自動割引を作成する:
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}

これで、新開発されたFunctionがデプロイ・バージョン管理され、旧Scriptからの置き換えが完了する。

チュートリアル 2: Shipping Script の置き換え (小計に応じた特定配送方法の制限)

よくあるScriptの例として、「大型品の特急配送コスト高騰を防ぐため、カートの注文小計が500ドルを超える場合は特急配送(Express)を表示候補から除外する」というケースについて、Delivery Customization APIによる実装例を以下に示す。

ステップ 2.1: 雛形(拡張機能)の生成


shopify app generate extension --template delivery_customization --name hide-express-fn

ステップ 2.2: インプット用 GraphQL クエリ (src/run.graphql)


query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}

ステップ 2.3: 処理ロジック (src/run.js — JavaScriptでの実装例)


// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}

ステップ 2.4: 管理画面を介した機能有効化 (GraphQLによる実行は不要)

配送のカスタマイズ(Delivery Customizations)には、Shopify標準の管理画面UIが用意されている。shopify app deploy実行後に以下の手順で設定を行う。

  1. 管理画面から 設定 → 配送と配達 へ移動する

  2. 最下部付近にある カスタマイズ セクションスクロールする

  3. カスタマイズを追加 ボタンをクリックし、デプロイ済みの該当Functionを選択する

  4. 変更内容を保存する

これにより、本番環境で配送の非表示ロジックの有効化が完了する。



Shopify delivery customization Function hiding shipping option at checkout

チュートリアル 3: Payment Script の置き換え (B2Bユーザーの「代金引換」制限)

従来のScriptの例として、「『B2B』タグを保持する顧客に対してチェックアウト画面から『代金引換』支払を選択肢として非表示にする」処理の、Payment Customization Function版の構築例を示す。

ステップ 3.1: 雛形(拡張機能)の生成


shopify app generate extension --template payment_customization --name hide-cod-b2b-fn

ステップ 3.2: インプット用 GraphQL クエリ


query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}

ステップ 3.3: 処理ロジック (src/run.js)



const NO_CHANGES = { operations: [] }; export function run(input) { const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0]; const isB2B = tagCheck?.hasTag === true; if (!isB2B) return NO_CHANGES; const codMethod = input.paymentMethods.find((pm) => pm.name.toLowerCase().includes("cash on delivery") ); if (!codMethod) return NO_CHANGES; return { operations: [{ hide: { paymentMethodId: codMethod.id } }], }; }

ステップ 3.4: 有効化の手順

決済のカスタマイズ(Payment Customizations)も同様に、管理画面の 設定 → 決済 → カスタマイズ セクションから設定用のUIが提供されている。配送設定の手順と同様に、該当のFunctionを選択して保存するのみで適用が完了する。

余談:購入完了後のフロー処理について

ここで、このブログの著者であるRevizeより1つ共有がある。我々が取り扱う領域は、FunctionAPIでは届かない「決済処理が完了した後の状態」である。注文が通った後に「商品を追加したい」「サイズ変更が必要になった」「配送先指定を直したい」「割引を適用し忘れた」といった顧客要望が発生することがある。Functionsはチェックアウト時のガードレールとして、Revizeは主に決済完了後の管理に位置づけられる。 Functionsはカートに追加可能な商品の是非や構成のルールを決め、Revizeは返金と再購入という不要な二度手間を踏ませることなく、顧客自身やサポートチームがシームレスに変更操作を実行できるように支援する。これは、手動対応では運用不可能なボリュームとなる大手のPlus事業者や高頻度取引ストア、あるいは優れた顧客体験を重視する事業者にとって、購入リピート率を守るために極めて不可欠な要素となり得る。

もしScriptsからFunctionsへの移行準備が現在進んでいても、上記のような購入後の追跡・変更の仕組みを整備できていなければ、数週間後に別のフローボトルネックに直面することになるだろう。我々が直近公開した注文管理設計ガイドには、ポストチェックアウト時における最良のシナリオを詳述している。

それでは、移行プロセスの解説に戻る。

安全に切り替える:テスト用タグパターンによる検証

現在のShopify Functionsには、管理画面から簡単に「下書き保存」やワンボタンでの切り替えのみが可能な「ドラフト実行モード」が備わっていない。推奨される標準的なテストパターンとして、新しいFunctionのロジック実行を「テスト用の顧客タグ」に紐づけて限定適用し、既存のScriptと同時に並行稼働させ、特定ユーザーに不具合がないことを実データで確認してから、全体への完全な切り替えに移行するというフローを踏むのが健全である。

手順 1: テスト用のタグを任意の顧客に割り振る

管理画面の 顧客管理 セクションから、自社内の確認用アカウントに FN-TESTER などの検証用タグを追加する。

手順 2: コード上で「タグ存在による分岐」を追加する


// run ファンクション内の開始部分にて判定を記述
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // タグがない場合は、既存のScript(判定処理)にフォールバックさせる
}

// 新しいテスト仕様となるFunction側の処理を以下へ続行記述

手順 3: インプット用クエリに hasAnyTag の取得を追加する


cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}

手順 4: 本番チェックアウト環境にて実地検証

タグの設定された確認用顧客アカウントでログインして実際に仕様を確認し、新規のFunctionによる分岐が正確に稼働することを確認する。一方でタグがない別の一般アカウントで検証し、既存の旧Script側の処理が適切に動くことを担保する。双方に不整合が発生せず数日間正常に動作することを確認できれば、分岐ロジック部分の記述(タグチェック)を削除し、全員に向けて完全適用する。

手順 5: 既存の古いScriptを無効化する

管理画面から アプリ管理 → Script Editor → [対象のScript] → 非公開(Unpublish) を実行する。非公開化されると、新規導入したFunction側が唯一の設定指標となる。

複数人で運用可能なスケーラブルなデプロイワークフロー

開発者のローカルPCなどから常にデプロイを手続きし続けるべきではない。最初のいくつか動作に必要な構築が完了した後は、継続的な自動統合(CI)パイプラインへとプロセスを移行させる。

構成可能なCIの例、最小構成ファイル


# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}

パートナーダッシュボードの 設定 → トークン 項目から必要な生成用トークン(Partner Token)を登録する。これにより、main ブランチへのマージを契機に、Functionsが安全に自動生成・適用される体制が構築できる。

バージョン管理とロールバック

shopify app deploy コマンドを実行するごとに、自動的に固有のスナップショットバージョンが作成・保持される。もしエラーが発生した際には、以下のコマンドにより容易に以前の履歴にロールバックできる。


shopify app versions list
shopify app release --version <前のバージョンID

Scriptsにおいて、旧コードを記憶またはバックアップから抽出し、管理画面に貼り付け直して戻していた工程と比べれば、圧倒的に整合性の取れた運用管理が可能となる。



Shopify Functions deployment pipeline across local staging and production environments

移行時に陥りがちなよくある失敗事例

我々がこれまで1年間にわたりPlus導入店舗へ伴走してきた実績から、よく見られる典型的な5つのミスタイプを以下に挙げる。

1. 既存のScriptと1対1でそっくり移植しようと試みること。 これは間違いである。記述構造をよく整理すれば、ひとつの優秀なFunctionのみに役割を統合できる余地がある。実装前にすべてのロジックパターンを一度見直すことが推奨される。

2. APIの読み書き用アクセス権限(アクセススコープ)の追加を忘れること。 多くのFunctionsは動作時に read_customers, read_orders, write_discounts などの設定スコープを必要とする。shopify.app.toml ファイル内に記述を追加した上で、アプリの管理者ページから再認証権限許可を実施していない場合、インプット用クエリが期待通りに動かず null を返すことになる。

3. 段階的なテストを行わず本番ユーザー全員へ一括反映させてしまうこと。 コードが一見完璧に書けているように見えても、実環境のイレギュラーな条件(空のカート、ギフト券決済併用時、注文エラー時の振る舞い、B2Bの下書き注文等)により予想外の事態が容易に発生する。テスト用タグによる段階的リリースを数日間挟むだけで、重大な障害事故のリスクをゼロに抑えることができる。

4. 「カスタマイズレポート」の一覧未確認。 本レポートは現在稼働しているScriptsの実態を網羅データとして最も的確に示すドキュメントである。開発チームの不確実な記憶からではなく、レポート上の明確な結果を手掛かりに進めることを徹底してほしい。

5. コレクションIDや顧客タグの値をコード中に直接ハードコードして直接書き込んでしまうこと。 マーチャント担当者側で制御を可変にするため、メタフィールドを活用した設定UIを挟み込むことが可能である。CLIを介してメタフィールドとの連携による制御構造を容易に構築できるため、詳細はShopifyの公式ドキュメント(Function Configuration)に目を通してほしい。

サービス完全移行までのガント検証スケジュール(各週ロードマップ)

残り75日を無事故で完全にクリアするためのモデル計画表を公開する。


週数

対応アクション

第1週 (今週から)

「カスタマイズレポート」を取得、解析の上、全Scriptsをリスト化。個々に独自のFunctionに移植するか、アプリに切り替えるか、廃止削減するか決定する。

第2~3週目

ローカル開発環境一連のセットアップ。最初の雛形のFunctionを作成し、最も仕様の単純な決済方法制御の非表示仕様を実装してみる。

第4~6週目

割引仕様の移植に着手。割引APIは考慮項目・取得範囲が最も多岐にわたる。並行的にテスト仕様によるチェックアウト検証を継続する。

第7~8週目

配送オプション、配送料制御の移植完了。管理UI上にて各テスト適用の設定を実施する。

第9~10週目

CI自動化デプロイパイプラインを作成。ローカル接続でのマニュアル管理から完全に脱却する。

第11週目

最後の総合稼働整合チェック。すべてのScriptの非公開化。約2週間Functionsの安定稼働状態を確認する。

6月30日当週

旧スクリプト廃止当日。事前に全対応を終えているためリスクなく安全に完了。

今週すぐに手を付け始めれば、十分なリカバリー期間を確保できる。ギリギリになってからでは間に合わない。



Week-by-week migration roadmap from Shopify Scripts to Functions

よくある質問(FAQ)

Functionsを利用するにはShopify Plus契約が必須か?

完全オリジナルの自社専用Functionを作成・適用する場合はShopify Plus契約が必要であるが、すでに他のベンダーから提供されApp Store上に公開されているアプリ内のFunctionsを利用する場合は、全プラン(ベーシック、スタンダード等)でそのまま利用可能である。 もしPlusを利用していない事業者であれば、要件を満たせる既存公開アプリから導入を行うか、自社独自のFunctionの開発が必要な場合はPlusへプランをアップグレードする必要がある。ただし、元々Scriptsを運用していたような大規模マーチャントであれば基本的にPlus環境で運用しているケースが大半を占めるため、本仕様による影響は軽微であるといえる。

TypeScriptを用いて記述することは可能か?

可能。TypeScriptは完全対応しており、CLI経由でプロジェクト設定ファイル等を一括生成できる。 コマンド shopify app generate extension を走らせ、出力先の選択として「JavaScript」を指定することで、コンパイルに必要な型定義ファイル import("../generated/api") がディレクトリ内に含まれる。必要に応じてファイルを .ts 拡張子へ書き換え、tsconfig.json などを構築に追記可能。ビルド後に生成されるWASMモジュールの仕様はどれを選択しても不変である。

処理性能(実行速度)はScriptsと比較してどの程度差があるか?

通常5ms以下。Rubyで動作していた従来のScript実行性能と比較すると、劇的に高速化されている。 FunctionsはWebAssemblyとして稼働する最適化されたコンパイルモジュールであるため、Shopify上での実行要件として「1動作5ms以下」を原則として規定している。もしこの制限時間を超えた場合は処理がエラーとなってスキップされ、何も結果を出力しない。実際、適切にビルドされたコードは1〜2ms程度で高速処理をパスできる構造になっている。実行時のパフォーマンスポテンシャルは旧来の比ではない。

Function内部から外部のAPIサーバー等へ通信してデータを取得できるか?

不可。Functions内部での外部ネットワーク要求は設計上限。 Functionsは純粋なコンピューティング処理(インプットとなるカート入力を元に、決められたフォーマットで瞬時にアウトプットオペレーションを返すこと)に役割を特化している。仮に外部連携データ(外部CRM情報のルックアップ、外部在庫状況のクエリ情報)が必要な場合、該当データを事前にメタフィールド上に顧客等に予め書き出して格納しておくか、あるいはアプリプロキシ(App Proxy)、Webhook、購入前Cart Transform APIのバックエンドサーバー処理などの別の解決策を選択する必要がある。移行検討プロセスにおいて、システム全体の再設計が必要とされる一番大きな理由がこの点にある。

Cart Transform API と Discounts API の切り分け目安は?

価格変動の算出(ディスカウント値の計算・適用)はDiscounts APIが担い、カート内の構成や商品そのものの切り替え・操作はCart Transform APIが担当する。 カート内の行数変更を行わずに単純な割引額のみを調整する場合は前者。2つの異なる商品のセット購入を契機に単一のセット商品(構成)へと自動的に置き換えを施す、あるいは単一のアイテム指定をバラ売り状態へ内部的にマッピング分解する等の制御が必要な場合は後者を利用する。古いScriptsではこれらを同じ場所で同時に処理させているケースがあったため、新しい設計では適切な役割へとプログラムを切り分ける必要がある。

別途開発ストアを新規に用意せずに、ローカル環境だけで完全な検証を行えるか?

単体でのユニットテストや、CLIでサンプルインプットを流し込んでの動作検証は cargo testnpm test あるいは shopify app function run コマンドだけで実施可能である。しかし、結合テストや実環境での仕様検証には、オンラインの開発ストアへの接続が必ず不可欠となる。 最終的な支払処理やUI表示変更などのチェックアウトでの実機を伴う動作の裏付けを行うには、実際に稼働する画面に触れる環境を用意することが必要となる。

同一仕様(ターゲット)の複数のFunctionを同時稼働させることは可能か?

可能。同ターゲット設定に対して機能を追加した場合、それらはあらかじめ規定された順序で実行される。 割引処理に関して、複数のFunctionが設定されているケースでは、Shopify標準の併用(自動適用・割引スタック順等の定義)ルールに従って処理され適用される。決済や配送カスタマイズ等の処理については順を追ってデータがパイプされパイプラインに受け渡される。ただ、要件や管理を極力シンプルにまとめるため、基本設計としては対象ターゲット毎に集約して定義することを推奨する。

新しいFunctionをデプロイした後、古い既存のScript側はどう稼働に影響するか?

Script Editor側の管理画面から明示的に非公開にするまで、双方の仕様が並行して同時にチェックアウト上に実行される。 動作の不整合・不具合を避けるために適切なチェック期間、検証設定を用意して対応する必要があるため、テストタグを設置して動作検証する手法が最も推奨される。並行稼働テストが無事終了すれば、旧Scripts側を管理画面から速やかに非公開化すること。2026年6月30日以降はこれらの設定状態によらず、Scripts自体が例外なくすべて完全に稼働停止する。

今回の切り替えにより、既存ECサイトのSEO評価やテーマ等のデザイン崩れに悪影響はあるか?

一切影響しない。Functionsはサーバーサイドのみで即座に計算され動作するため、顧客の前で構築されているフロントエンドテーマの表示要素や、商品ページのマークアップ情報、構造化記述データを含め一切干渉しない。 カスタマイズの影響範囲はあくまでチェックアウト画面内、支払方法の決定、配送料情報の反映等の部分に制限される。

カートラインオブジェクトに設定されたカスタムプロパティ(カスタム属性データ)にアクセスしていたScriptはどう移行すべきか?

GraphQLクエリ内からカート内の対象オブジェクトに対して attribute フィールドを追加・定義することで容易に取得できる。 チェックアウトのライン情報配下に attribute(key: "your-key") { value } をクエリに差し込むことで、従来のRubyで実行していた引数メソッド等の手順をそのまま置き換える形で情報を拾うことが可能となる。

処理結果情報に基づく、アナリティクス計測や、注文完了後に注文内容に対してタグを付与する等の書き込み作業を直接Functionから行えるか?

不可能。Functions自体は「カートの内容に対して出力するアクション情報を返すのみ」の完全に純粋で独立したモジュールであり、自身の内部からタグ等の情報を書き換えるためのデータ発行やWebhook等の直接イベントを起動させることはできない。 タグの変更対応等のバックヤード的な調整が稼働に必要な場合、注文完了時のシステム動作イベントをきっかけとして別の仕掛け(Shopify Flow)などでタグのコントロールや更新情報の送出などをカバーすることを推奨する。

自作アプリを一から開発せずとも要件を満たせる、オープンな既存の代替公開アプリはストア上に提供されているか?

豊富に用意されている。Shopifyアプリストアには優秀な対応ツール・システムが非常に多くすでに並んでいる。 「割引 Function」「配送 追加 カスタマイズ」「決済カスタマイズ」等の類似キーでストアを検索してほしい。ボリューム割引、タグ情報に基づく決済制限等の一般的なユースケースであれば、十分にそれらの外部パッケージを使い回すだけで追加の開発期間とコストを大幅に削減できる。完全に独自の複雑な内製ポリシーでない限り、そのような既存仕様の適合案を十分に優先検討することを推奨する。

仮に移行が間に合わず、6月30日の期限を過ぎてシステムを放置した場合に起こるリスクは?

稼働していた仕組みは有無を言わさずその瞬間から作動しなくなる。延命処理や、救済猶予期間は一切存在しない。 割引、制限を掛けていた特殊な配送料情報、非表示処理を加えていた支払方法等の設定は、UTC日付で7月1日0時をもってすべて機能不全に陥り、管理されていないありのままの条件(一般決済、標準配送)で即座に決済に回ることになる。売上額等への実被害や注文トラフィック上の致命的な損失が生じる可能性が極めて高く、このリスク要因は何よりも致命的になりかねない。移行プロセス全体の工程ボリュームと、想定より工数が掛かることを考慮して、期限に対して余裕のある計画を進めるのが必須である。

移行が完全に正常完了した時点で、旧アプリ「Script Editor」を管理画面から直ちに削除して問題ないか?

削除して全く問題ない。移行を完結してFunctionsのみで運営する状態が完全にできていれば、アプリは不要になる。不都合がなければ当セクションが完全無効化・廃除される期限前に直ちにアンインストール手続きを行ってよい。

今週から着手すべきアクション

この詳細記事をただ単に読み終わって、作業用PCのブラウザを闭じて満足することのないように進めてほしい。即行動できる事項として、以下の4つの課題は一歩として最初の一週間以内に完了させよう。

1. 稼働状況をレポートで完全に可視化する。 ECストアの管理画面内の「設定 → チェックアウト → カスタマイズレポート」にアクセスし、データをダウンロード保存すること。これがそのままあなたの行うべき移行工程の優先リストとなる。

2. ローカルの開発用のプログラム整備。 Node.js 18+ の設置、Shopify CLI、Rustの開発用設定手順を確認のうえ、shopify version がエラーを出さずに動作することを検証する。15〜30分もあれば全て完了する。

3. 仮のFunction(モジュール)を開発ストア上で試しにデプロイしてみる。 最も構成の理解が容易な、決済方法の限定ルール等をダミーとして一から動作検証ベースで完結させてみるだけで構わない。実際に本番稼働へと適用しなくても、そのプロセスを通せる一連のツールチェーン動作が保証されれば多大なる進捗と信頼感に繋がる。

4. 今後8週間分、開発スケジュールに対象のタスク用確保枠をカレンダー等に固定して設ける。 優先移行プロセスは定常業務の合間の手隙で行うべき軽微な案件として処理してはならない。社内の定期スプリント、およびリリースサイクルを意識し、工数の優先枠として確立して取り組んでほしい。

実際に計画通りに正常に移行を終わらせたクライアント達を見ていると、対応の型は常に類似を見せている。「事前調査・設計に要した約2週間、実際の機能開発と実装調整におよそ3週間、不具合修正やタグ等を用いたテスト管理検証におよそ1週間」。合計6週間。およそ10週間ある残りのタイミングから鑑みて、テストプロセスや確認作業の品質を最良まで仕上げる為、まだ十分にこのスケジュールに収まるバッファ、機会を活かすことができる。

チェックアウトにおけるカート仕様が綺麗に整った後に、次に多くの大規模チームが目を向ける課題がまさにポストチェックアウト(配送先住所の迅速なケア変更、購入後の別のサイズ、カラー差し替え、注文後の個別プロモーション適用など)である。自サイトにおける最高の顧客購買体験への設計、改善をこの機会に検討される場合は、この全ての新規Functionsと共に滑らかに調和しながら連携動作可能なRevizeがすでにShopifyアプリストアから公開中であるため、ぜひ併せて参考材料として活用してほしい。

関連リファレンス(公式ドキュメント等一覧)


こちらの関連ドキュメント記事もよく参照されています


2026年4月16日。昨日(4月15日)、ShopifyはScript Editorを完全にロックした。もはや新しいScriptの作成や公開はできない。実行停止は75日後の2026年6月30日に迫っている。

もしあなたが、この移行を過去12ヶ月間「次のスプリント」に先送りし続けてきたShopify Plus開発者や、Plusストアを運営するパートナー代理店なら、問題が発生している。それも「後で直せばいい」というレベルではない。「7月1日の午前0時にチェックアウトが機能しなくなる」という問題だ。ほとんどのPlusストアは、何年もかけて5〜20個のScriptを蓄積しており、それらは誰が書いたかも忘れ去られたまま、バックグラウンドで割引ルール、配送方法の非表示、あるいは決済ゲートウェイの制御を静かに処理している。

このガイドは、私たちが今年1月に存在していてほしかったと感じた技術移行マニュアルである。 戦略論だけでなく、実際のコードに踏み込んで解説する。読み終える頃には、Shopify CLIを使用したFunctionの雛形作成から、割引、配送カスタマイズ、決済カスタマイズのためのRustまたはJavaScriptによるロジック記述、タグ付けされた特定の顧客グループに対する安全なテスト、そして既存のチェックアウトを破損させることなくプロダクション環境へデプロイする方法までを理解できるようになる。

さあ、ストアからScriptsの一掃を始め、代わりにFunctionsを設定しよう。



Developer migrating Shopify Scripts to Shopify Functions modules

クイック確認:ScriptsからFunctionsへの移行(60秒要約)

1段落での要約: Shopify Scripts(Script Editor内のRubyコード、Plus限定)は、Shopify Functions(RustまたはJavaScriptで記述され、すべてのプランで利用可能なWebAssemblyモジュール)に置き換わる。shopify app generate extensionを使用してFunctionの基本構造を生成し、必要なカートデータを取得するrun.graphqlクエリを作成、さらに処理結果(割引、配送方法の非表示など)を返すrun.rsまたはrun.jsファイルを記述する。その後、shopify app deployでデプロイし、管理画面またはGraphQLミューテーションを通じて有効化する。FunctionsはコンパイルされたWASMとして5ms未満の低レイテンシで実行され、すべてのプランに対応する。これがShopifyが今後サポートする唯一のカスタマイズパスである。

6月30日に実際に起こること

コード作成を始める前に、スケジュールを確実に把握する必要がある。重要な日付は2つある。


日付

発生する事象

必要な対応

2026年4月15日 (超過)

Script Editorが閲覧専用に。新しいScriptの作成、既存Scriptの編集は不可。

既存のScriptは稼働を維持。移行を開始するか、既存ロジックを確定させる必要がある。

2026年6月30日

すべてのShopify Scriptsが実行を停止する。

この日より前に、代替となるFunctionを本番環境で稼働させる必要がある。

この移行の結果は二者択一である。6月30日までにFunctionがデプロイされ、チェックアウトが正常に機能し続けるか、あるいはデプロイされず、影響を受けるすべてのカートで標準価格、標準配送レート、かつ有効なすべての決済方法にエラーなくデフォルト状態で戻るかのどちらかだ。妥協案は存在しない。Scriptが実行されるか、されないかであり、6月30日以降は完全に実行されなくなる。

ヒント: Shopify管理画面から「設定 → チェックアウト → カスタマイズレポート」を開いてほしい。ここには、ストアで実行中のすべてのScript、その機能、および推奨される代替Functionのタイプがリストアップされている。まずはここから始めよう。

FunctionsとScriptsの違い




項目

Shopify Scripts (廃止予定)

Shopify Functions (代替機能)

開発言語

Ruby DSL (Shopify独自の仕様)

Rust, JavaScript, TypeScript

実行環境

Shopifyインフラ上のサンドボックス環境(Ruby)

WebAssembly (WASM) — 5ms未満の超高速実行

対象プラン

Plusのみ

すべてのプラン(カスタムアプリによる実装はPlus、公開アプリは全プラン開放)

エディタ

管理画面内のScript Editor

ローカルIDE + Shopify CLI

バージョン管理

なし — 直接編集

Git対応 — 完全なバージョン履歴管理

テスト方法

チェックアウト画面での手動検証

shopify app devによるローカル環境テスト、プレビューリンク検証

デプロイ

管理画面上で「保存」を実行

ターミナルからshopify app deployを実行

制御対象

ラインアイテム、配送、決済

割引、Cart Transform、バリデーション、配送カスタマイズ、決済カスタマイズ、注文ルーティング、フルフィルメント制約など多数

設計思想の変化は重大である。Scriptsは「テキストエリア内でRubyをいじる」ものだったが、Functionsは「本格的なアプリを書く。バージョン管理を行い、ローカルでテストし、CIパイプラインでデプロイする」というプロセスになる。学習曲線はより急だが、チェックアウトロジックとしての移行はこれで最後になる見通しだ。Functionsは、Scriptsのような一時しのぎの対応ではなく、Shopifyが長期にわたってコミットする標準仕様である。

お使いのScriptsと対応するFunctionタイプのマップ

現在お使いのすべてのScriptは、正確にひとつのFunction APIへとマッピングされる。以下が参照すべき移行一覧である。


従来のScriptタイプ

従来の動作内容

移行先のFunction API

ターゲット

Line Item Script

特定の商品、顧客、またはカートの条件に応じた割引適用

Cart & Checkout Discounts API

cart.lines.discounts.generate.run

Shipping Script(割引)

カート内の条件に基づく無料または割引配送の提供

Cart & Checkout Discounts API

cart.delivery-options.discounts.generate.run

Shipping Script(非表示 / リネーム / 並び替え)

設定値以上のカートに対する配送料の非表示や、「通常便」を「50ドル以上で送料無料」へ変更

Delivery Customization API

cart.delivery-options.transform.run

Payment Script

B2B向けのPayPal非表示、一定額以上の代金引換の非表示、表示順の並び替え

Payment Customization API

cart.payment-methods.transform.run

カート内容変更Script(稀)

商品セット(バンドル)の作成、ラインアイテムの置換

Cart Transform API

cart.transform.run

チェックアウト拒否Script

無効なSKU組み合わせ時にチェックアウト画面をブロック

Cart & Checkout Validation API

cart.validations.generate.run

もし10個のScriptを運用している場合、それらは3〜5個のFunctionに統合できる可能性が高い。複数の記述をひとつのFunction内の洗練された条件分岐ロジックへ整理できるためである。



Shopify Functions unifying discounts, delivery, and payment customizations

前提条件: ローカル開発環境のセットアップ

Functionの構築を開始する前に、以下の3つの環境をローカル端末にインストールし、ターミナルで確認を行う必要がある。

1. Node.js 18+


node --version
# 18.0.0 以上必須

バージョンが古い場合は、nvmを介してインストールするか、nodejs.orgからダウンロードすること。

2. Shopify CLI 3+


npm install -g @shopify/cli@latest
shopify version
# 3.x 以上の出力を確認

3. Rust ツールチェーン (RustでFunctionを開発する場合のみ必要)


curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32-wasip1
cargo --version

JavaScriptでFunctionを作成する場合、Rustは不要。開発チーム内でいずれかの言語を選択し、統一して管理することを推奨する。混在させると、メンテナンスコストが増大する。

4. 開発用ストア

Shopifyパートナーダッシュボードから新規開発用ストアを作成するか、既存の開発用ストアを利用する。本番環境に適用する前に、まずここで検証を行う。

最初のFunctionを生成する

基本的な構成ファイルの生成はCLIに任せることができる。任意の作業ディレクトリで以下を実行する。


# 新規Shopifyアプリの初期化 (既存のアプリがない場合のみ実行)
shopify app init my-checkout-functions
cd my-checkout-functions

# Function拡張機能の生成
shopify app generate extension

CLIプロンプトの手順に従って設定を入力する。割引Functionを作成する場合は、以下のオプションを選択する。

  • タイプ: Function

  • テンプレート: discount (あるいは cart_checkout_validation, delivery_customization, payment_customization など選択可能)

  • 言語: Rust または JavaScript

  • 名称: 任意の名前(例:volume-discount-fn

この操作により、extensions/volume-discount-fn/ ディレクトリ配下に、以下の構成ファイルが生成される。


extensions/volume-discount-fn/
├── shopify.extension.toml      # Functionの設定(ターゲット、ビルド、バージョン等)
├── src/
├── cart_lines_discounts_generate_run.graphql   # インプット用クエリ
└── cart_lines_discounts_generate_run.rs        # Functionの処理ロジック
├── Cargo.toml                  # Rustの依存関係管理(Rust開発時のみ)
└── README.md

以降、開発中に編集を繰り返すことになるファイルは .toml(設定)、.graphql(インプット)、そして .rs または .js(処理ロジック)の3点のみである。

チュートリアル 1: Line Item Script の置き換え (ボリューム割引)

例として、特定のコレクションに属する商品が「カート内に5点以上」追加された際、小計から10%割引を適用する従来のScriptを、Functionとして置き換える手順を示す。

ステップ 1.1: 設定ファイルの記述 (shopify.extension.toml)


api_version = "2026-01"

[[extensions]]
name = "volume-discount-fn"
handle = "volume-discount-fn"
type = "function"

[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart_lines_discounts_generate_run"

[extensions.build]
command = "cargo build --target=wasm32-wasip1 --release"
path = "target/wasm32-wasip1/release/volume-discount-fn.wasm"
watch = ["src/**/*.rs"]

Step 1.2: インプット用 GraphQL クエリ (src/cart_lines_discounts_generate_run.graphql)


query Input {
  cart {
    lines {
      id
      quantity
      cost {
        subtotalAmount {
          amount
        }
      }
      merchandise {
        ... on ProductVariant {
          product {
            inAnyCollection(ids: ["gid://shopify/Collection/123456789"])
          }
        }
      }
    }
  }
  discount {
    discountClasses
  }
}

ヒント: Functionは、GraphQLでクエリを定義したデータのみを認識する。不要なフィールドを除外してクエリデータを最小限に維持することが、実行速度向上とリソース節約に繋がる。

ステップ 1.3: ロジック記述 (src/cart_lines_discounts_generate_run.rs)


use super::schema;
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function]
fn cart_lines_discounts_generate_run(
    input: schema::cart_lines_discounts_generate_run::Input,
) -> Result<schema::CartLinesDiscountsGenerateRunResult> {
    // 割引区分がマッチしない場合は処理を中断
    let has_order_discount = input
        .discount()
        .discount_classes()
        .contains(&schema::DiscountClass::Order);

    if !has_order_discount {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 割引対象のコレクション内商品の合計数量を算出
    let qualifying_qty: i64 = input
        .cart()
        .lines()
        .iter()
        .filter(|line| {
            if let schema::Merchandise::ProductVariant(v) = line.merchandise() {
                *v.product().in_any_collection()
            } else {
                false
            }
        })
        .map(|line| *line.quantity())
        .sum();

    if qualifying_qty < 5 {
        return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] });
    }

    // 注文小計から10%引きを適用
    Ok(schema::CartLinesDiscountsGenerateRunResult {
        operations: vec![schema::CartOperation::OrderDiscountsAdd(
            schema::OrderDiscountsAddOperation {
                selection_strategy: schema::OrderDiscountSelectionStrategy::First,
                candidates: vec![schema::OrderDiscountCandidate {
                    targets: vec![schema::OrderDiscountCandidateTarget::OrderSubtotal(
                        schema::OrderSubtotalTarget {
                            excluded_cart_line_ids: vec![],
                        },
                    )],
                    message: Some("Volume discount: 10% off".to_string()),
                    value: schema::OrderDiscountCandidateValue::Percentage(
                        schema::Percentage { value: Decimal(10.0) }
                    ),
                    conditions: None,
                    associated_discount_code: None,
                }],
            },
        )],
    })
}

ステップ 1.4: テスト、デプロイ、有効化


# ホットリロード対応のローカルテスト起動
shopify app dev

# 準備完了後、本番へデプロイ
shopify app deploy

# デプロイ後に表示されるGraphiQLコンソール (開発ターミナルで `g` キーを押下する) にて、
# 作成したFunctionを利用する自動割引を作成する:
mutation {
  discountAutomaticAppCreate(
    automaticAppDiscount: {
      title: "Volume Discount (5+ collection items)"
      functionHandle: "volume-discount-fn"
      discountClasses: [ORDER]
      startsAt: "2026-04-16T00:00:00Z"
    }
  ) {
    automaticAppDiscount { discountId }
    userErrors { field message }
  }
}

これで、新開発されたFunctionがデプロイ・バージョン管理され、旧Scriptからの置き換えが完了する。

チュートリアル 2: Shipping Script の置き換え (小計に応じた特定配送方法の制限)

よくあるScriptの例として、「大型品の特急配送コスト高騰を防ぐため、カートの注文小計が500ドルを超える場合は特急配送(Express)を表示候補から除外する」というケースについて、Delivery Customization APIによる実装例を以下に示す。

ステップ 2.1: 雛形(拡張機能)の生成


shopify app generate extension --template delivery_customization --name hide-express-fn

ステップ 2.2: インプット用 GraphQL クエリ (src/run.graphql)


query Input {
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
    deliveryGroups {
      deliveryOptions {
        handle
        title
      }
    }
  }
}

ステップ 2.3: 処理ロジック (src/run.js — JavaScriptでの実装例)


// @ts-check
/**
 * @typedef {import("../generated/api").RunInput} RunInput
 * @typedef {import("../generated/api").FunctionRunResult} FunctionRunResult
 */

const NO_CHANGES = { operations: [] };
const THRESHOLD = 500.0;
const HIDE_TITLES = ["Express", "Overnight"];

/**
 * @param {RunInput} input
 * @returns {FunctionRunResult}
 */
export function run(input) {
  const subtotal = parseFloat(input.cart.cost.subtotalAmount.amount);
  if (subtotal < THRESHOLD) return NO_CHANGES;

  const operations = input.cart.deliveryGroups.flatMap((group) =>
    group.deliveryOptions
      .filter((opt) => HIDE_TITLES.some((t) => opt.title.includes(t)))
      .map((opt) => ({
        hide: { deliveryOptionHandle: opt.handle },
      }))
  );

  return { operations };
}

ステップ 2.4: 管理画面を介した機能有効化 (GraphQLによる実行は不要)

配送のカスタマイズ(Delivery Customizations)には、Shopify標準の管理画面UIが用意されている。shopify app deploy実行後に以下の手順で設定を行う。

  1. 管理画面から 設定 → 配送と配達 へ移動する

  2. 最下部付近にある カスタマイズ セクションスクロールする

  3. カスタマイズを追加 ボタンをクリックし、デプロイ済みの該当Functionを選択する

  4. 変更内容を保存する

これにより、本番環境で配送の非表示ロジックの有効化が完了する。



Shopify delivery customization Function hiding shipping option at checkout

チュートリアル 3: Payment Script の置き換え (B2Bユーザーの「代金引換」制限)

従来のScriptの例として、「『B2B』タグを保持する顧客に対してチェックアウト画面から『代金引換』支払を選択肢として非表示にする」処理の、Payment Customization Function版の構築例を示す。

ステップ 3.1: 雛形(拡張機能)の生成


shopify app generate extension --template payment_customization --name hide-cod-b2b-fn

ステップ 3.2: インプット用 GraphQL クエリ


query Input {
  cart {
    buyerIdentity {
      customer {
        hasTags(tags: [{ tag: "B2B" }]) {
          tag
          hasTag
        }
      }
    }
  }
  paymentMethods {
    id
    name
  }
}

ステップ 3.3: 処理ロジック (src/run.js)



const NO_CHANGES = { operations: [] }; export function run(input) { const tagCheck = input.cart?.buyerIdentity?.customer?.hasTags?.[0]; const isB2B = tagCheck?.hasTag === true; if (!isB2B) return NO_CHANGES; const codMethod = input.paymentMethods.find((pm) => pm.name.toLowerCase().includes("cash on delivery") ); if (!codMethod) return NO_CHANGES; return { operations: [{ hide: { paymentMethodId: codMethod.id } }], }; }

ステップ 3.4: 有効化の手順

決済のカスタマイズ(Payment Customizations)も同様に、管理画面の 設定 → 決済 → カスタマイズ セクションから設定用のUIが提供されている。配送設定の手順と同様に、該当のFunctionを選択して保存するのみで適用が完了する。

余談:購入完了後のフロー処理について

ここで、このブログの著者であるRevizeより1つ共有がある。我々が取り扱う領域は、FunctionAPIでは届かない「決済処理が完了した後の状態」である。注文が通った後に「商品を追加したい」「サイズ変更が必要になった」「配送先指定を直したい」「割引を適用し忘れた」といった顧客要望が発生することがある。Functionsはチェックアウト時のガードレールとして、Revizeは主に決済完了後の管理に位置づけられる。 Functionsはカートに追加可能な商品の是非や構成のルールを決め、Revizeは返金と再購入という不要な二度手間を踏ませることなく、顧客自身やサポートチームがシームレスに変更操作を実行できるように支援する。これは、手動対応では運用不可能なボリュームとなる大手のPlus事業者や高頻度取引ストア、あるいは優れた顧客体験を重視する事業者にとって、購入リピート率を守るために極めて不可欠な要素となり得る。

もしScriptsからFunctionsへの移行準備が現在進んでいても、上記のような購入後の追跡・変更の仕組みを整備できていなければ、数週間後に別のフローボトルネックに直面することになるだろう。我々が直近公開した注文管理設計ガイドには、ポストチェックアウト時における最良のシナリオを詳述している。

それでは、移行プロセスの解説に戻る。

安全に切り替える:テスト用タグパターンによる検証

現在のShopify Functionsには、管理画面から簡単に「下書き保存」やワンボタンでの切り替えのみが可能な「ドラフト実行モード」が備わっていない。推奨される標準的なテストパターンとして、新しいFunctionのロジック実行を「テスト用の顧客タグ」に紐づけて限定適用し、既存のScriptと同時に並行稼働させ、特定ユーザーに不具合がないことを実データで確認してから、全体への完全な切り替えに移行するというフローを踏むのが健全である。

手順 1: テスト用のタグを任意の顧客に割り振る

管理画面の 顧客管理 セクションから、自社内の確認用アカウントに FN-TESTER などの検証用タグを追加する。

手順 2: コード上で「タグ存在による分岐」を追加する


// run ファンクション内の開始部分にて判定を記述
let is_tester = input
    .cart()
    .buyer_identity()
    .and_then(|bi| bi.customer())
    .map(|c| c.has_any_tag())
    .unwrap_or(&false);

if !*is_tester {
    return Ok(default_result);  // タグがない場合は、既存のScript(判定処理)にフォールバックさせる
}

// 新しいテスト仕様となるFunction側の処理を以下へ続行記述

手順 3: インプット用クエリに hasAnyTag の取得を追加する


cart {
  buyerIdentity {
    customer {
      hasAnyTag(tags: ["FN-TESTER"])
    }
  }
}

手順 4: 本番チェックアウト環境にて実地検証

タグの設定された確認用顧客アカウントでログインして実際に仕様を確認し、新規のFunctionによる分岐が正確に稼働することを確認する。一方でタグがない別の一般アカウントで検証し、既存の旧Script側の処理が適切に動くことを担保する。双方に不整合が発生せず数日間正常に動作することを確認できれば、分岐ロジック部分の記述(タグチェック)を削除し、全員に向けて完全適用する。

手順 5: 既存の古いScriptを無効化する

管理画面から アプリ管理 → Script Editor → [対象のScript] → 非公開(Unpublish) を実行する。非公開化されると、新規導入したFunction側が唯一の設定指標となる。

複数人で運用可能なスケーラブルなデプロイワークフロー

開発者のローカルPCなどから常にデプロイを手続きし続けるべきではない。最初のいくつか動作に必要な構築が完了した後は、継続的な自動統合(CI)パイプラインへとプロセスを移行させる。

構成可能なCIの例、最小構成ファイル


# .github/workflows/deploy-functions.yml
name: Deploy Shopify Functions
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '20' }
      - uses: dtolnay/rust-toolchain@stable
        with: { targets: wasm32-wasip1 }
      - run: npm install -g @shopify/cli@latest
      - run: shopify app deploy --force
        env:
          SHOPIFY_CLI_PARTNERS_TOKEN: ${{ secrets.SHOPIFY_CLI_PARTNERS_TOKEN }}

パートナーダッシュボードの 設定 → トークン 項目から必要な生成用トークン(Partner Token)を登録する。これにより、main ブランチへのマージを契機に、Functionsが安全に自動生成・適用される体制が構築できる。

バージョン管理とロールバック

shopify app deploy コマンドを実行するごとに、自動的に固有のスナップショットバージョンが作成・保持される。もしエラーが発生した際には、以下のコマンドにより容易に以前の履歴にロールバックできる。


shopify app versions list
shopify app release --version <前のバージョンID

Scriptsにおいて、旧コードを記憶またはバックアップから抽出し、管理画面に貼り付け直して戻していた工程と比べれば、圧倒的に整合性の取れた運用管理が可能となる。



Shopify Functions deployment pipeline across local staging and production environments

移行時に陥りがちなよくある失敗事例

我々がこれまで1年間にわたりPlus導入店舗へ伴走してきた実績から、よく見られる典型的な5つのミスタイプを以下に挙げる。

1. 既存のScriptと1対1でそっくり移植しようと試みること。 これは間違いである。記述構造をよく整理すれば、ひとつの優秀なFunctionのみに役割を統合できる余地がある。実装前にすべてのロジックパターンを一度見直すことが推奨される。

2. APIの読み書き用アクセス権限(アクセススコープ)の追加を忘れること。 多くのFunctionsは動作時に read_customers, read_orders, write_discounts などの設定スコープを必要とする。shopify.app.toml ファイル内に記述を追加した上で、アプリの管理者ページから再認証権限許可を実施していない場合、インプット用クエリが期待通りに動かず null を返すことになる。

3. 段階的なテストを行わず本番ユーザー全員へ一括反映させてしまうこと。 コードが一見完璧に書けているように見えても、実環境のイレギュラーな条件(空のカート、ギフト券決済併用時、注文エラー時の振る舞い、B2Bの下書き注文等)により予想外の事態が容易に発生する。テスト用タグによる段階的リリースを数日間挟むだけで、重大な障害事故のリスクをゼロに抑えることができる。

4. 「カスタマイズレポート」の一覧未確認。 本レポートは現在稼働しているScriptsの実態を網羅データとして最も的確に示すドキュメントである。開発チームの不確実な記憶からではなく、レポート上の明確な結果を手掛かりに進めることを徹底してほしい。

5. コレクションIDや顧客タグの値をコード中に直接ハードコードして直接書き込んでしまうこと。 マーチャント担当者側で制御を可変にするため、メタフィールドを活用した設定UIを挟み込むことが可能である。CLIを介してメタフィールドとの連携による制御構造を容易に構築できるため、詳細はShopifyの公式ドキュメント(Function Configuration)に目を通してほしい。

サービス完全移行までのガント検証スケジュール(各週ロードマップ)

残り75日を無事故で完全にクリアするためのモデル計画表を公開する。


週数

対応アクション

第1週 (今週から)

「カスタマイズレポート」を取得、解析の上、全Scriptsをリスト化。個々に独自のFunctionに移植するか、アプリに切り替えるか、廃止削減するか決定する。

第2~3週目

ローカル開発環境一連のセットアップ。最初の雛形のFunctionを作成し、最も仕様の単純な決済方法制御の非表示仕様を実装してみる。

第4~6週目

割引仕様の移植に着手。割引APIは考慮項目・取得範囲が最も多岐にわたる。並行的にテスト仕様によるチェックアウト検証を継続する。

第7~8週目

配送オプション、配送料制御の移植完了。管理UI上にて各テスト適用の設定を実施する。

第9~10週目

CI自動化デプロイパイプラインを作成。ローカル接続でのマニュアル管理から完全に脱却する。

第11週目

最後の総合稼働整合チェック。すべてのScriptの非公開化。約2週間Functionsの安定稼働状態を確認する。

6月30日当週

旧スクリプト廃止当日。事前に全対応を終えているためリスクなく安全に完了。

今週すぐに手を付け始めれば、十分なリカバリー期間を確保できる。ギリギリになってからでは間に合わない。



Week-by-week migration roadmap from Shopify Scripts to Functions

よくある質問(FAQ)

Functionsを利用するにはShopify Plus契約が必須か?

完全オリジナルの自社専用Functionを作成・適用する場合はShopify Plus契約が必要であるが、すでに他のベンダーから提供されApp Store上に公開されているアプリ内のFunctionsを利用する場合は、全プラン(ベーシック、スタンダード等)でそのまま利用可能である。 もしPlusを利用していない事業者であれば、要件を満たせる既存公開アプリから導入を行うか、自社独自のFunctionの開発が必要な場合はPlusへプランをアップグレードする必要がある。ただし、元々Scriptsを運用していたような大規模マーチャントであれば基本的にPlus環境で運用しているケースが大半を占めるため、本仕様による影響は軽微であるといえる。

TypeScriptを用いて記述することは可能か?

可能。TypeScriptは完全対応しており、CLI経由でプロジェクト設定ファイル等を一括生成できる。 コマンド shopify app generate extension を走らせ、出力先の選択として「JavaScript」を指定することで、コンパイルに必要な型定義ファイル import("../generated/api") がディレクトリ内に含まれる。必要に応じてファイルを .ts 拡張子へ書き換え、tsconfig.json などを構築に追記可能。ビルド後に生成されるWASMモジュールの仕様はどれを選択しても不変である。

処理性能(実行速度)はScriptsと比較してどの程度差があるか?

通常5ms以下。Rubyで動作していた従来のScript実行性能と比較すると、劇的に高速化されている。 FunctionsはWebAssemblyとして稼働する最適化されたコンパイルモジュールであるため、Shopify上での実行要件として「1動作5ms以下」を原則として規定している。もしこの制限時間を超えた場合は処理がエラーとなってスキップされ、何も結果を出力しない。実際、適切にビルドされたコードは1〜2ms程度で高速処理をパスできる構造になっている。実行時のパフォーマンスポテンシャルは旧来の比ではない。

Function内部から外部のAPIサーバー等へ通信してデータを取得できるか?

不可。Functions内部での外部ネットワーク要求は設計上限。 Functionsは純粋なコンピューティング処理(インプットとなるカート入力を元に、決められたフォーマットで瞬時にアウトプットオペレーションを返すこと)に役割を特化している。仮に外部連携データ(外部CRM情報のルックアップ、外部在庫状況のクエリ情報)が必要な場合、該当データを事前にメタフィールド上に顧客等に予め書き出して格納しておくか、あるいはアプリプロキシ(App Proxy)、Webhook、購入前Cart Transform APIのバックエンドサーバー処理などの別の解決策を選択する必要がある。移行検討プロセスにおいて、システム全体の再設計が必要とされる一番大きな理由がこの点にある。

Cart Transform API と Discounts API の切り分け目安は?

価格変動の算出(ディスカウント値の計算・適用)はDiscounts APIが担い、カート内の構成や商品そのものの切り替え・操作はCart Transform APIが担当する。 カート内の行数変更を行わずに単純な割引額のみを調整する場合は前者。2つの異なる商品のセット購入を契機に単一のセット商品(構成)へと自動的に置き換えを施す、あるいは単一のアイテム指定をバラ売り状態へ内部的にマッピング分解する等の制御が必要な場合は後者を利用する。古いScriptsではこれらを同じ場所で同時に処理させているケースがあったため、新しい設計では適切な役割へとプログラムを切り分ける必要がある。

別途開発ストアを新規に用意せずに、ローカル環境だけで完全な検証を行えるか?

単体でのユニットテストや、CLIでサンプルインプットを流し込んでの動作検証は cargo testnpm test あるいは shopify app function run コマンドだけで実施可能である。しかし、結合テストや実環境での仕様検証には、オンラインの開発ストアへの接続が必ず不可欠となる。 最終的な支払処理やUI表示変更などのチェックアウトでの実機を伴う動作の裏付けを行うには、実際に稼働する画面に触れる環境を用意することが必要となる。

同一仕様(ターゲット)の複数のFunctionを同時稼働させることは可能か?

可能。同ターゲット設定に対して機能を追加した場合、それらはあらかじめ規定された順序で実行される。 割引処理に関して、複数のFunctionが設定されているケースでは、Shopify標準の併用(自動適用・割引スタック順等の定義)ルールに従って処理され適用される。決済や配送カスタマイズ等の処理については順を追ってデータがパイプされパイプラインに受け渡される。ただ、要件や管理を極力シンプルにまとめるため、基本設計としては対象ターゲット毎に集約して定義することを推奨する。

新しいFunctionをデプロイした後、古い既存のScript側はどう稼働に影響するか?

Script Editor側の管理画面から明示的に非公開にするまで、双方の仕様が並行して同時にチェックアウト上に実行される。 動作の不整合・不具合を避けるために適切なチェック期間、検証設定を用意して対応する必要があるため、テストタグを設置して動作検証する手法が最も推奨される。並行稼働テストが無事終了すれば、旧Scripts側を管理画面から速やかに非公開化すること。2026年6月30日以降はこれらの設定状態によらず、Scripts自体が例外なくすべて完全に稼働停止する。

今回の切り替えにより、既存ECサイトのSEO評価やテーマ等のデザイン崩れに悪影響はあるか?

一切影響しない。Functionsはサーバーサイドのみで即座に計算され動作するため、顧客の前で構築されているフロントエンドテーマの表示要素や、商品ページのマークアップ情報、構造化記述データを含め一切干渉しない。 カスタマイズの影響範囲はあくまでチェックアウト画面内、支払方法の決定、配送料情報の反映等の部分に制限される。

カートラインオブジェクトに設定されたカスタムプロパティ(カスタム属性データ)にアクセスしていたScriptはどう移行すべきか?

GraphQLクエリ内からカート内の対象オブジェクトに対して attribute フィールドを追加・定義することで容易に取得できる。 チェックアウトのライン情報配下に attribute(key: "your-key") { value } をクエリに差し込むことで、従来のRubyで実行していた引数メソッド等の手順をそのまま置き換える形で情報を拾うことが可能となる。

処理結果情報に基づく、アナリティクス計測や、注文完了後に注文内容に対してタグを付与する等の書き込み作業を直接Functionから行えるか?

不可能。Functions自体は「カートの内容に対して出力するアクション情報を返すのみ」の完全に純粋で独立したモジュールであり、自身の内部からタグ等の情報を書き換えるためのデータ発行やWebhook等の直接イベントを起動させることはできない。 タグの変更対応等のバックヤード的な調整が稼働に必要な場合、注文完了時のシステム動作イベントをきっかけとして別の仕掛け(Shopify Flow)などでタグのコントロールや更新情報の送出などをカバーすることを推奨する。

自作アプリを一から開発せずとも要件を満たせる、オープンな既存の代替公開アプリはストア上に提供されているか?

豊富に用意されている。Shopifyアプリストアには優秀な対応ツール・システムが非常に多くすでに並んでいる。 「割引 Function」「配送 追加 カスタマイズ」「決済カスタマイズ」等の類似キーでストアを検索してほしい。ボリューム割引、タグ情報に基づく決済制限等の一般的なユースケースであれば、十分にそれらの外部パッケージを使い回すだけで追加の開発期間とコストを大幅に削減できる。完全に独自の複雑な内製ポリシーでない限り、そのような既存仕様の適合案を十分に優先検討することを推奨する。

仮に移行が間に合わず、6月30日の期限を過ぎてシステムを放置した場合に起こるリスクは?

稼働していた仕組みは有無を言わさずその瞬間から作動しなくなる。延命処理や、救済猶予期間は一切存在しない。 割引、制限を掛けていた特殊な配送料情報、非表示処理を加えていた支払方法等の設定は、UTC日付で7月1日0時をもってすべて機能不全に陥り、管理されていないありのままの条件(一般決済、標準配送)で即座に決済に回ることになる。売上額等への実被害や注文トラフィック上の致命的な損失が生じる可能性が極めて高く、このリスク要因は何よりも致命的になりかねない。移行プロセス全体の工程ボリュームと、想定より工数が掛かることを考慮して、期限に対して余裕のある計画を進めるのが必須である。

移行が完全に正常完了した時点で、旧アプリ「Script Editor」を管理画面から直ちに削除して問題ないか?

削除して全く問題ない。移行を完結してFunctionsのみで運営する状態が完全にできていれば、アプリは不要になる。不都合がなければ当セクションが完全無効化・廃除される期限前に直ちにアンインストール手続きを行ってよい。

今週から着手すべきアクション

この詳細記事をただ単に読み終わって、作業用PCのブラウザを闭じて満足することのないように進めてほしい。即行動できる事項として、以下の4つの課題は一歩として最初の一週間以内に完了させよう。

1. 稼働状況をレポートで完全に可視化する。 ECストアの管理画面内の「設定 → チェックアウト → カスタマイズレポート」にアクセスし、データをダウンロード保存すること。これがそのままあなたの行うべき移行工程の優先リストとなる。

2. ローカルの開発用のプログラム整備。 Node.js 18+ の設置、Shopify CLI、Rustの開発用設定手順を確認のうえ、shopify version がエラーを出さずに動作することを検証する。15〜30分もあれば全て完了する。

3. 仮のFunction(モジュール)を開発ストア上で試しにデプロイしてみる。 最も構成の理解が容易な、決済方法の限定ルール等をダミーとして一から動作検証ベースで完結させてみるだけで構わない。実際に本番稼働へと適用しなくても、そのプロセスを通せる一連のツールチェーン動作が保証されれば多大なる進捗と信頼感に繋がる。

4. 今後8週間分、開発スケジュールに対象のタスク用確保枠をカレンダー等に固定して設ける。 優先移行プロセスは定常業務の合間の手隙で行うべき軽微な案件として処理してはならない。社内の定期スプリント、およびリリースサイクルを意識し、工数の優先枠として確立して取り組んでほしい。

実際に計画通りに正常に移行を終わらせたクライアント達を見ていると、対応の型は常に類似を見せている。「事前調査・設計に要した約2週間、実際の機能開発と実装調整におよそ3週間、不具合修正やタグ等を用いたテスト管理検証におよそ1週間」。合計6週間。およそ10週間ある残りのタイミングから鑑みて、テストプロセスや確認作業の品質を最良まで仕上げる為、まだ十分にこのスケジュールに収まるバッファ、機会を活かすことができる。

チェックアウトにおけるカート仕様が綺麗に整った後に、次に多くの大規模チームが目を向ける課題がまさにポストチェックアウト(配送先住所の迅速なケア変更、購入後の別のサイズ、カラー差し替え、注文後の個別プロモーション適用など)である。自サイトにおける最高の顧客購買体験への設計、改善をこの機会に検討される場合は、この全ての新規Functionsと共に滑らかに調和しながら連携動作可能なRevizeがすでにShopifyアプリストアから公開中であるため、ぜひ併せて参考材料として活用してほしい。

関連リファレンス(公式ドキュメント等一覧)


こちらの関連ドキュメント記事もよく参照されています


RevizeでShopifyストアを刷新しましょう。顧客体験を軸にリードする。

© 著作権 2024、無断転載を禁じます

RevizeでShopifyストアを刷新しましょう。顧客体験を軸にリードする。

© 著作権 2024、無断転載を禁じます

RevizeでShopifyストアを刷新しましょう。顧客体験を軸にリードする。

© 著作権 2024、無断転載を禁じます

RevizeでShopifyストアを刷新しましょう。顧客体験を軸にリードする。

© 著作権 2024、無断転載を禁じます