Skip to content

Keep the cart store correct for custom fields, mixed bodies and variant swaps - #4133

Open
mdamjanic7 wants to merge 4 commits into
previewfrom
preview-fix/4a-cart-route-and-fragment
Open

mdamjanic7 wants to merge 4 commits into
previewfrom
preview-fix/4a-cart-route-and-fragment

Conversation

@mdamjanic7

@mdamjanic7 mdamjanic7 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Apps that change the cart from code (Shopify.actions.updateCart) hit three silent gaps:

  • Custom CartFragment fields go stale. The store only copies id, checkout URL, totals and cost from mutation responses, so fields like appliedGiftCards stay outdated after a change.
  • Mixed cart route bodies are half-applied. A body with both lines and attributes runs one mutation and silently drops the other.
  • Variant swaps don't work. The route drops merchandiseId on line updates. Once it's forwarded, Shopify can return the swapped line under a new ID, and the store loses it.

The cart skill also has no guidance for code-driven changes, so apps call the cart route directly and bypass the store.

WHAT is this pull request doing?

  • Mutation responses now update every non-line field, under the existing ordering guards.
  • The cart route rejects mixed bodies with 400 invalid_cart_request, and the store rolls back.
  • Line updates forward merchandiseId. If the response no longer contains the submitted line ID, the store takes the lines from the response, or, when other changes overlap, keeps the line until the refresh they trigger.
  • hydrogen-cart-ui documents code-driven changes: the Standard Actions prerequisite, when the store is ready, one kind of change per call, and one line per update, removal or swap (additions can batch).

Trade-offs / behavior changes

  • Mixed bodies are now rejected. Callers send one kind of change per request.
  • A lines mutation now replaces discount codes and other returned non-line fields instead of keeping stale copies.
  • quantity stays required on line updates (Standard Actions requires it, and the store projects from it).
  • A separate PR, multi-line changes and changes during the first load, lifts the one-line limit. The two are independent and merge cleanly.

HOW to test your changes?

pnpm --filter @shopify/hydrogen test. It covers custom fields across mutations, mixed-body rejection, and variant swaps returned under the same or a new line ID.

@shopify

shopify Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Oxygen deployed a preview of your preview-fix/4a-cart-route-and-fragment branch. Details:

Storefront Status Preview link Deployment details Last update (UTC)
Skeleton (skeleton.hydrogen.shop) ✅ Successful (Logs) Preview deployment Inspect deployment October 2, 202612:09 AM

Learn more about Hydrogen's GitHub integration.

The cart server handlers return the app's full CartFragment on every
mutation, but the store copied only id, checkoutUrl, updatedAt,
totalQuantity and cost from the response. Custom fields stayed stale after
a change and were missing on a cart created by the first add. The store now
takes every non-line field from the latest settled response, under the
existing ordering and revalidation guards.
parseJsonBody picked the first of note, discountCodes, attributes and lines
and ignored the rest. Standard Actions accepts all of them in one updateCart
call and dispatches one event per kind, so the store projected every change
while the server applied only one. A body with more than one of these fields
is now an invalid_cart_request, so every projection rolls back and the caller
sees the error.
@mdamjanic7
mdamjanic7 force-pushed the preview-fix/4a-cart-route-and-fragment branch from ea2094f to 04017f4 Compare October 1, 2026 22:51
@mdamjanic7
mdamjanic7 marked this pull request as ready for review October 1, 2026 23:03
@mdamjanic7
mdamjanic7 requested a review from a team as a code owner October 1, 2026 23:03
@mdamjanic7 mdamjanic7 self-assigned this Oct 1, 2026
@mdamjanic7
mdamjanic7 force-pushed the preview-fix/4a-cart-route-and-fragment branch from 04017f4 to 3eea9d9 Compare October 1, 2026 23:54
partitionLines classified a JSON line with both id and merchandiseId as an
update but copied only quantity, attributes and sellingPlanId, so a variant
swap through the cart route changed only the quantity. Updates now carry
merchandiseId to cartLinesUpdate, and CartLineUpdateInput declares it.

Shopify can return the swapped line under a new line ID. The store only
matched the submitted ID, so the line disappeared. When no other work
overlaps the change, the store now takes the lines from the response.
Otherwise it keeps the line until the revalidation that overlapping work
already triggers, so a failed refresh doesn't lose it. A missing line only
means removal when the change was a removal.
…ons prerequisite

The cart UI skill covered forms and refresh() only, so code that changes the
cart without a form (gift rules, sweeps, attribute writers) had no guidance
and tended to hand-roll fetch calls. Document the Standard Actions runtime
prerequisite and how to use Shopify.actions.updateCart with the store.
@mdamjanic7
mdamjanic7 force-pushed the preview-fix/4a-cart-route-and-fragment branch from 3eea9d9 to 6f7db3d Compare October 2, 2026 00:08

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant