--- title: Migrate from deprecated discount APIs to the Discount API description: >- Learn how to migrate your existing order, product, and shipping discount Functions to the Discount API. With the Discount API, you can create a single discount that applies to product prices, order totals, and shipping costs. source_url: html: >- https://shopify.dev/docs/apps/build/discounts/migrate-discount-api?extension=rust&appType=graphql md: >- https://shopify.dev/docs/apps/build/discounts/migrate-discount-api.md?extension=rust&appType=graphql --- # Migrate from deprecated discount APIs to the Discount API This tutorial demonstrates how to migrate your existing Discount Functions to the unified [Discount API](https://shopify.dev/docs/api/functions/reference/discount). You will need to update your existing Function's logic and configuration UI to accommodate schema changes. Caution After you migrate your Function, you will no longer be able to deploy your Function using deprecated Product, Order, and Shipping APIs and you won't be able to use Shopify Admin Graphql API before version 2025-04. You can manage distributed versions of your app using the [Partner Dashboard](https://partners.shopify.com). ### What you'll learn In this tutorial, you'll learn how to do the following tasks: * Update your Function configuration * Update your Function logic * Use GraphQL to create and update discounts * Use Admin UI extensions to create and update discounts * Use a React Router app to create and update discounts ## Requirements [You have a Shopify app with existing discount Functions using the Product, Order and Shipping API](https://shopify.dev/docs/apps/build/discounts#discount-classes) You have a Shopify app with existing discount Functions using the [Product](https://shopify.dev/docs/api/functions/reference/product-discounts), [Order](https://shopify.dev/docs/api/functions/reference/order-discounts), or [Shipping](https://shopify.dev/docs/api/functions/reference/shipping-discounts) API. ## Project Rust GraphQL [View on GitHub](https://github.com/Shopify/discounts-reference-app/blob/main/examples/javascript/default) ## Update the Function configuration The new Discount API requires changes to your Function's configuration settings. You'll start by updating your API version to access the new features, then modify your Function targets to work with the unified discount system. These configuration updates are the foundation for enabling multi-class discounts in your Function. ### Update the API version In your `shopify.extension.toml` file, update to the latest stable API version. This example uses `2025-04`. ## extensions/discount-function/shopify.extension.toml ```toml api_version = "2025-04" [[extensions]] name = "t:name" handle = "discount-function-rs" type = "function" description = "t:description" [[extensions.targeting]] target = "cart.lines.discounts.generate.run" input_query = "src/cart_lines_discounts_generate_run.graphql" export = "cart_lines_discounts_generate_run" [[extensions.targeting]] target = "cart.delivery-options.discounts.generate.run" input_query = "src/cart_delivery_options_discounts_generate_run.graphql" export = "cart_delivery_options_discounts_generate_run" [extensions.build] command = "cargo build --target=wasm32-wasip1 --release" path = "target/wasm32-wasip1/release/discount-function-rs.wasm" watch = [ "src/**/*.rs" ] ``` ### Update your Function targets The Discount API enables you to target multiple discount classes with a single discount, so you can create one discount that reduces costs on orders, products, and shipping. To update your existing Discount Functions to use the Discount API, you'll need to update the run [targets](https://shopify.dev/docs/api/functions/reference/discount/graphql). The following are the new run targets define where your discount can be applied: * `cart.lines.discounts.generate.run`: Applies discounts to cart lines and order subtotal. * `cart.delivery-options.discounts.generate.run`: Applies discounts to delivery options. For detailed specifications of these targets see the [extension targets documentation](https://shopify.dev/docs/api/functions/reference/discount/graphql). ## extensions/discount-function/shopify.extension.toml ```toml api_version = "2025-04" [[extensions]] name = "t:name" handle = "discount-function-rs" type = "function" description = "t:description" [[extensions.targeting]] target = "cart.lines.discounts.generate.run" input_query = "src/cart_lines_discounts_generate_run.graphql" export = "cart_lines_discounts_generate_run" [[extensions.targeting]] target = "cart.delivery-options.discounts.generate.run" input_query = "src/cart_delivery_options_discounts_generate_run.graphql" export = "cart_delivery_options_discounts_generate_run" [extensions.build] command = "cargo build --target=wasm32-wasip1 --release" path = "target/wasm32-wasip1/release/discount-function-rs.wasm" watch = [ "src/**/*.rs" ] ``` ## Update the Function logic To compare the Discount API schema with the deprecated [Product](https://shopify.dev/docs/api/functions/latest/product-discount), [Order](https://shopify.dev/docs/api/functions/latest/order-discount), and [Shipping](https://shopify.dev/docs/api/functions/latest/shipping-discount) API schemas, consult the docs on [migrating from deprecated discount Function APIs](https://shopify.dev/docs/api/functions/latest/discount#migrate-from-deprecated-discount-function-apis). ### Update the Function logic for cart Discounts The Discount API requires you to update your Function. This example returns [`operations`](https://shopify.dev/docs/api/functions/reference/discount/graphql/common-objects/cartoperation) which contain [`orderDiscountsAdd`](https://shopify.dev/docs/api/functions/reference/discount/graphql/common-objects/orderdiscountsaddoperation) and [`productDiscountsAdd`](https://shopify.dev/docs/api/functions/reference/discount/graphql/common-objects/productdiscountsaddoperation). ## extensions/discount-function/src/cart\_lines\_discounts\_generate\_run.rs ```rust 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 { let max_cart_line = input .cart() .lines() .iter() .max_by(|a, b| { a.cost() .subtotal_amount() .amount() .partial_cmp(b.cost().subtotal_amount().amount()) .unwrap_or(std::cmp::Ordering::Equal) }) .ok_or("No cart lines found")?; let has_order_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); let has_product_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Product); if !has_order_discount_class && !has_product_discount_class { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } let mut operations = vec![]; // Check if the discount has the ORDER class if has_order_discount_class { operations.push(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("10% OFF ORDER".to_string()), value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(10.0), }), conditions: None, associated_discount_code: None, }], }, )); } // Check if the discount has the PRODUCT class if has_product_discount_class { operations.push(schema::CartOperation::ProductDiscountsAdd( schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates: vec![schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine( schema::CartLineTarget { id: max_cart_line.id().clone(), quantity: None, }, )], message: Some("20% OFF PRODUCT".to_string()), value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(20.0), }), associated_discount_code: None, }], }, )); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) } ``` ### Update the Function logic for delivery Discounts The new Discount API requires you to update function. This example returns [`operations`](https://shopify.dev/docs/api/functions/reference/discount/graphql/common-objects/deliveryoperation) which contain [`deliveryDiscountsAdd`](https://shopify.dev/docs/api/functions/reference/discount/graphql/common-objects/deliverydiscountsaddoperation). ## extensions/discount-function/src/cart\_delivery\_options\_discounts\_generate\_run.rs ```rust use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result { let has_shipping_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Shipping); if !has_shipping_discount_class { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); } let first_delivery_group = input .cart() .delivery_groups() .first() .ok_or("No delivery groups found")?; Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryGroup( schema::DeliveryGroupTarget { id: first_delivery_group.id().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(100.0), }), message: Some("FREE DELIVERY".to_string()), associated_discount_code: None, }], }, )], }) } ``` ### Update the input query fields If your Function's [input query](https://shopify.dev/docs/api/functions/reference/discount/graphql/input?api%5Bversion%5D=unstable) requests `discountNode`, then update the field from `discountNode` to [`discount`](https://shopify.dev/docs/api/functions/reference/discount/graphql/common-objects/discount). ### Re-generate the schema ## Terminal shopify app function schema If you modified your Function's input or output, you must regenerate the new Discount API schema to support the new properties that are available to the Function input and output. ## Manage Shopify app versions In the Partner Dashboard, you can manage which app versions are released to your dev store and to the stores that have installed your app. You should test the migrated Function on your dev store before releasing it to the stores that have installed your app. 1. In the [Partner Dashboard](https://partners.shopify.com), click **Apps**. 2. Select your app. 3. In the sidebar, click **Versions**, and select the version that you want to release to the stores that have installed your app. 4. On the version's page, click **Release**. This version will be active on stores that have installed your app in production. ![The Partner Dashboard with the Versions section highlighted.](https://shopify.dev/assets/assets/apps/discounts/partner-versions-BnM29KTh.png) 5. In the sidebar, click **Extensions** and turn on the **dev store preview** toggle. ![The Partner Dashboard with the Versions and Extensions sections highlighted.](https://shopify.dev/assets/assets/apps/discounts/partner-dev-toggle-c8iagqVf.png) You can now deploy your migrated Function to your dev store and test it. The version that you released to the stores that have installed your app will be the version that is active on those stores. When you start your development server from the Shopify CLI, your developement store will access the version hosted by your development server. ### Start your app to preview changes Caution After deploying your migrated Function, you'll no longer be able to use the legacy Product, Order, and Shipping APIs for it. You'll also no longer be able to use the Shopify Admin GraphQL API before version 2025-04. 1. Save your updated configuration TOML file. 2. Start `app dev` if it's not already running: ## Terminal ```bash shopify app dev ``` The configuration TOML file changes will be applied automatically on the development store. ## Create the discount with Graphi​QL, UI extensions, or a React Router app In this section, you'll learn how to create a discount using the GraphiQL interface, and how to build a UI that allows merchants to configure the discount using Admin UI extensions or a React Router app. You can choose the option that best fits your app in the App Type picker at the top of the page. ### Create the discount with Graphi​QL In this step, you'll use the GraphiQL interface to create a product, order and shipping discount backed by the Function you deployed in the previous step. This involves querying your Function's ID and using it to create the discount with the appropriate GraphQL mutation. ### Use the Graphi​QL interface to create a discount 1. Open the GraphiQL interface using the Shopify CLI by pressing `g`. 2. In the GraphiQL interface, in the **API Version** field, select version **2025-10** or higher. ### Get the handle of your Function 1. Use your function handle in GraphQL mutations instead of querying for the function ID. Your function handle is defined in `shopify.extension.toml` as `handle`: ## shopify.extension.toml ```toml [[extensions]] name = "discount-function" handle = "discount-function-rs" type = "function" ``` Note If you're upgrading to API version 2025-10 or later, you no longer need to query for function IDs. Use your stable function handle instead, which remains consistent across environments. ### Create the discount Use the [`discountAutomaticAppCreate`](https://shopify.dev/docs/api/admin-graphql/latest/mutations/discountAutomaticAppCreate) mutation to create an automatic discount. 1. Add the Shopify Function handle from the [previous step](https://shopify.dev/docs/apps/build/discounts/build-discount-function?extension=rust#query-the-shopify-function) as the value for the `functionHandle` input. 2. Add the `discountClasses` field to your mutation. You should receive a GraphQL response that includes the ID of the created product discount. If the response includes any messages under `userErrors`, then review the errors, check that your mutation and `functionHandle` are correct, and try the request again. Troubleshooting If you receive a `Could not find Function` error, then confirm the following: * The Function handle is correct. * You've installed the app on your development store. * [Development store preview](#preview-the-function-on-a-development-store) is enabled in the Dev Dashboard. * Your app has the `write_discounts` access scope. ## discountAutomaticAppCreate.graphql ```graphql mutation { discountAutomaticAppCreate( automaticAppDiscount: { title: "Cart line, Order, Shipping discount" functionId: "YOUR_FUNCTION_ID_HERE" discountClasses: [PRODUCT, ORDER, SHIPPING] startsAt: "2025-01-01T00:00:00" } ) { automaticAppDiscount { discountId } userErrors { field message } } } ``` After using GraphiQL to create the discount, you can test the discount and the newly migrated function. ## Test the discount and the newly migrated function ### Trigger the discount on your dev store 1. From your Shopify admin, click **Discounts**. You should now see the **Cart line, order and shipping** that you created. ![A list of all active discounts for the store.](https://shopify.dev/assets/assets/apps/discounts/functions-discount-list-multi-class-bMqqn_8b.png) 2. Deactivate or delete all other discounts to ensure that the **Cart line, order and shipping** discount is the only active discount. 3. Open your dev store and build a cart with a single item in it. After you navigate to the checkout page and fill in your shipping address, you'll see the discounts applied to the shipping rates. ### Review the Function execution 1. In the terminal where `shopify app dev` is running, review your Function executions. When [testing Functions on development stores](https://shopify.dev/docs/apps/build/functions/test-debug-functions#test-your-function-on-a-development-store), the `dev` output shows Function executions, debug logs you've added, and a link to a local file containing full execution details. 2. In a new terminal window, use the Shopify CLI command [`app function replay`](https://shopify.dev/docs/api/shopify-cli/app/app-function-replay) to [replay a Function execution locally](https://shopify.dev/docs/apps/build/functions/test-debug-functions#execute-the-function-locally-using-shopify-cli). This lets you debug your Function without triggering it again on Shopify. ## Terminal ```terminal shopify app function replay ``` 3. Select the Function execution from the top of the list. Press `q` to quit when you are finished debugging. ## Deploy your app When you're ready to release your changes to users, you can create and release an [app version](https://shopify.dev/docs/apps/launch/deployment/app-versions). An app version is a snapshot of your app configuration and all extensions. 1. Navigate to your app directory. 2. Run the following command. Optionally, you can provide a name or message for the version using the `--version` and `--message` flags. ## Terminal ```terminal shopify app deploy ``` Releasing an app version replaces the current active version that's served to stores that have your app installed. It might take several minutes for app users to be upgraded to the new version. Tip If you want to create a version, but avoid releasing it to users, then run the `deploy` command with a `--no-release` flag. You can release the unreleased app version using Shopify CLI's [`release`](https://shopify.dev/docs/api/shopify-cli/app/app-release) command, or through the Dev Dashboard. ## extensions/discount-function/shopify.extension.toml ```toml api_version = "2025-04" [[extensions]] name = "t:name" handle = "discount-function-rs" type = "function" description = "t:description" [[extensions.targeting]] target = "cart.lines.discounts.generate.run" input_query = "src/cart_lines_discounts_generate_run.graphql" export = "cart_lines_discounts_generate_run" [[extensions.targeting]] target = "cart.delivery-options.discounts.generate.run" input_query = "src/cart_delivery_options_discounts_generate_run.graphql" export = "cart_delivery_options_discounts_generate_run" [extensions.build] command = "cargo build --target=wasm32-wasip1 --release" path = "target/wasm32-wasip1/release/discount-function-rs.wasm" watch = [ "src/**/*.rs" ] ``` ## extensions/discount-function/src/cart\_lines\_discounts\_generate\_run.rs ```rust 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 { let max_cart_line = input .cart() .lines() .iter() .max_by(|a, b| { a.cost() .subtotal_amount() .amount() .partial_cmp(b.cost().subtotal_amount().amount()) .unwrap_or(std::cmp::Ordering::Equal) }) .ok_or("No cart lines found")?; let has_order_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Order); let has_product_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Product); if !has_order_discount_class && !has_product_discount_class { return Ok(schema::CartLinesDiscountsGenerateRunResult { operations: vec![] }); } let mut operations = vec![]; // Check if the discount has the ORDER class if has_order_discount_class { operations.push(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("10% OFF ORDER".to_string()), value: schema::OrderDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(10.0), }), conditions: None, associated_discount_code: None, }], }, )); } // Check if the discount has the PRODUCT class if has_product_discount_class { operations.push(schema::CartOperation::ProductDiscountsAdd( schema::ProductDiscountsAddOperation { selection_strategy: schema::ProductDiscountSelectionStrategy::First, candidates: vec![schema::ProductDiscountCandidate { targets: vec![schema::ProductDiscountCandidateTarget::CartLine( schema::CartLineTarget { id: max_cart_line.id().clone(), quantity: None, }, )], message: Some("20% OFF PRODUCT".to_string()), value: schema::ProductDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(20.0), }), associated_discount_code: None, }], }, )); } Ok(schema::CartLinesDiscountsGenerateRunResult { operations }) } ``` ## extensions/discount-function/src/cart\_delivery\_options\_discounts\_generate\_run.rs ```rust use super::schema; use shopify_function::prelude::*; use shopify_function::Result; #[shopify_function] fn cart_delivery_options_discounts_generate_run( input: schema::cart_delivery_options_discounts_generate_run::Input, ) -> Result { let has_shipping_discount_class = input .discount() .discount_classes() .contains(&schema::DiscountClass::Shipping); if !has_shipping_discount_class { return Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![] }); } let first_delivery_group = input .cart() .delivery_groups() .first() .ok_or("No delivery groups found")?; Ok(schema::CartDeliveryOptionsDiscountsGenerateRunResult { operations: vec![schema::DeliveryOperation::DeliveryDiscountsAdd( schema::DeliveryDiscountsAddOperation { selection_strategy: schema::DeliveryDiscountSelectionStrategy::All, candidates: vec![schema::DeliveryDiscountCandidate { targets: vec![schema::DeliveryDiscountCandidateTarget::DeliveryGroup( schema::DeliveryGroupTarget { id: first_delivery_group.id().clone(), }, )], value: schema::DeliveryDiscountCandidateValue::Percentage(schema::Percentage { value: Decimal(100.0), }), message: Some("FREE DELIVERY".to_string()), associated_discount_code: None, }], }, )], }) } ``` ## extensions/discount-function/src/cart\_delivery\_options\_discounts\_generate\_run.graphql ```graphql query Input { cart { deliveryGroups { id } } discount { discountClasses metafield( namespace: "$app:example-discounts--ui-extension" key: "function-configuration" ) { jsonValue } } } ``` ## discountAutomaticAppCreate.graphql ```graphql mutation { discountAutomaticAppCreate( automaticAppDiscount: { title: "Cart line, Order, Shipping discount" functionId: "YOUR_FUNCTION_ID_HERE" discountClasses: [PRODUCT, ORDER, SHIPPING] startsAt: "2025-01-01T00:00:00" } ) { automaticAppDiscount { discountId } userErrors { field message } } } ``` ## Tutorial complete! You've successfully migrated your Discount Functions to the new Discount API. Now, you can use this Function to apply discounts that target cart lines, order subtotals, and shipping rates. *** ### Next Steps [Build a UI extension to configure your discount Function\ \ Add configuration to your discounts experience using metafields and build a user interface for your Function.](https://shopify.dev/docs/apps/build/discounts/build-ui-extension)[Build a React Router app to configure your discount Function\ \ Add configuration to your discounts experience using metafields and build a user interface for your Function.](https://shopify.dev/docs/apps/build/discounts/build-ui-with-react-router)[Review the UX guidelines\ \ Review the UX guidelines to learn how to implement discounts in user interfaces.](https://shopify.dev/docs/apps/build/discounts/ux-for-discounts)[Learn more about Shopify Functions\ \ Learn more about how Shopify Functions work and the benefits of using Shopify Functions.](https://shopify.dev/docs/apps/build/functions)[Consult the Shopify Functions API references\ \ Consult the API references for Shopify Functions](https://shopify.dev/docs/api/functions)[Learn more about deploying app versions\ \ Learn more about deploying app versions to Shopify](https://shopify.dev/docs/apps/launch/deployment/deploy-app-versions)