How to Update Automatic Discounts Using GraphQL in a Shopify Remix App

How to Update Automatic Discounts Using GraphQL in a Shopify Remix App

Programmatic management of discounts is a vital function of many Shopify applications. When working on an integrated app using the Shopify Remix Template, it is necessary to work through updates to automatic discounts through the Shopify Admin GraphQL API from admin authentication.

The purpose of this guide is to outline how to successfully update automatic discounts in Inline Remix loaders using GraphQL mutations.

Hire Shopify Developers

Understand the Shopify GraphQL Mutation

Automatic discounts are divided by Shopify into various types of discounts. Depending on the type of discount structure you need, certain mutations are to be used:

  • discountAutomaticBasicUpdate: Used for standard “Amount Off” automatic discounts (e.g., $10 off a specific collection).
  • discountAutomaticBxgyUpdate: This is used for automatic discounts of type “Buy X Get Y”.
  • discountAutomaticAppUpdate: Used if your discount is managed by a custom Shopify Function (advanced/dynamic rules).

We will concentrate on executing an ordinary automatic discount update (discountAutomaticBasicUpdate).

The GraphQL Mutation Syntax

mutation discountAutomaticBasicUpdate($id: ID!, $automaticBasicDiscount: DiscountAutomaticBasicInput!) {
  discountAutomaticBasicUpdate(id: $id, automaticBasicDiscount: $automaticBasicDiscount) {
    automaticDiscountNode {
      id
      automaticDiscount {
        ... on DiscountAutomaticBasic {
          title
          status
          startsAt
          endsAt
        }
      }
    }
    userErrors {
      field
      message
    }
  }
}

Implement the Update Mutation in a Remix Route

In the Shopify Remix framework, the mutations that are caused by user actions, such as pressing the “save changes” button, must be executed in a Remix action function.

Here is how you handle the update process securely using @shopify/shopify-app-remix:

// app/routes/app.discount.$id.tsx
import type { ActionFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useActionData, useSubmit, useLoaderData } from "@remix-run/react";
import { authenticate } from "../shopify.server"; // Your Shopify server configuration

// Action handles the POST/PUT requests from the form submission
export const action = async ({ request }: ActionFunctionArgs) => {
  // 1. Authenticate the admin request
  const { admin } = await authenticate.admin(request);
  
  // 2. Parse the form data sent from the frontend
  const formData = await request.formData();
  const discountId = formData.get("discountId") as string;
  const newTitle = formData.get("title") as string;
  const newEndsAt = formData.get("endsAt") as string; // Expecting ISO string or null

  try {
    // 3. Execute the GraphQL mutation using the authenticated admin instance
    const response = await admin.graphql(
      `#graphql
      mutation discountAutomaticBasicUpdate($id: ID!, $automaticBasicDiscount: DiscountAutomaticBasicInput!) {
        discountAutomaticBasicUpdate(id: $id, automaticBasicDiscount: $automaticBasicDiscount) {
          automaticDiscountNode {
            id
          }
          userErrors {
            field
            message
          }
        }
      }`,
      {
        variables: {
          id: discountId,
          automaticBasicDiscount: {
            title: newTitle,
            endsAt: newEndsAt || null
          },
        },
      }
    );

    const responseJson = await response.json();
    const errors = responseJson.data?.discountAutomaticBasicUpdate?.userErrors;

    if (errors && errors.length > 0) {
      return json({ success: false, errors }, { status: 400 });
    }

    return json({ success: true, errors: [] });
  } catch (error) {
    return json({ success: false, errors: [{ message: "Failed to update discount." }] }, { status: 500 });
  }
};

export default function EditDiscountRoute() {
  const actionData = useActionData<typeof action>();
  const submit = useSubmit();

  const handleSave = () => {
    const formData = new FormData();
    formData.append("discountId", "gid://shopify/DiscountAutomaticNode/123456789");
    formData.append("title", "Updated Flash Sale - 20% Off");
    formData.append("endsAt", "2026-12-31T23:59:59Z");

    submit(formData, { method: "POST" });
  };

  return (
    <div style={{ padding: "20px" }}>
      <h1>Update Automatic Discount</h1>
      <button onClick={handleSave}>Save Changes</button>
      
      {actionData?.success && <p style={{ color: "green" }}>Discount updated successfully!</p>}
      {actionData?.errors && actionData.errors.map((err, idx) => (
        <p key={idx} style={{ color: "red" }}>{err.message}</p>
      ))}
    </div>
  );
}

Best Practices to Keep in Mind

Make sure Scopes are Defined

Whenever you need to create or update a discount via the GraphQL Admin API, the access scope must be correctly set up in your Shopify app (shopify.app.toml).

scopes = "write_discounts,read_discounts"

Note: When you upload these scopes in between app development, you must authenticate the app and refresh your server locally.

Working with User Errors

The Shopify GraphQL mutation does not return standard HTTP errors for business processing errors (e.g., setting an expiration date in the past). Instead, it returns an array called userErrors. An important fact to keep in mind is to check for userErrors in your Remix actions before you send any success response to the UI.

Global ID Formats

Make sure you are passing the fully qualified Shopify Global ID (GID) to the id argument (e.g., “gid://shopify/DiscountAutomaticNode/123456789”), and not just the raw numeric string.

Conclusion

To summarize the information above, the process of updating automatic discounts in GraphQL via the Shopify Remix app is straightforward as long as one understands the principles of Shopify mutations and Shopify Remix server-side setup. The developers can utilize the Shopify admin context to validate their requests, run corresponding GraphQL mutations, check for userErrors, and send the relevant Global ID to create adequate discount management capabilities for merchants.

No matter if it is a flash sale promotion or creating a custom dashboard, this solution guarantees that the Shopify app will be secure and customizable.

Shopify Development Services

FAQ

1. What mutation do I need to use for automatic discount update?

Different mutations are appropriate to various discount types: discountAutomaticBasicUpdate is used for automatic discount, discountAutomaticBxgyUpdate is for Buy X Get Y discount, discountAutomaticAppUpdate is used for discount relying on Shopify functions.

2. Why do I need to use action() from Remix?

action() provides secure authentication for admin sessions in a server-side manner and uses GraphQL mutations without disclosing access tokens at the client level.

Previous Article

How to Start a Magento Store From Scratch in 2026 

Next Article

Top 10 Magento Development Company in India

Write a Comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Get Connect With Us

Subscribe to our email newsletter to get the latest posts delivered right to your email.
Pure inspiration, zero spam ✨