Shopify GraphQL API migration: four deprecation waves, four codemods
shopify graphql migration 49 tracked change records
Shopify does not break its APIs in big SDK majors. It retires surface on a dated-version schedule — 2026-07, 2026-10 — and the changes land inside GraphQL documents rather than SDK method names: a field renamed on the Customer Account API, an entire query subtree removed, a deprecated mutation argument, a new enum value that existing exhaustive switches do not handle, and a POS UI extensions print call replaced by a namespaced successor. Each of these breaks consuming code in a way a lockfile bump never mentions, and each is mechanical enough to carry as a codemod. mendapi ships four deterministic packs for these waves, all applied locally as reviewable diffs.
The four packs, exactly as the registry ships them
This list is read directly from the mendapi migration pack registry at page build time, so it matches what the codemods actually do:
Shopify Customer Account API: discountedUnitPrice -> approximateDiscountedUnitPrice
Pack shopify-customer-account-draftorder-price:
- Rename discountedUnitPrice field to approximateDiscountedUnitPrice in Customer Account API GraphQL usage
Upstream changelog: https://shopify.dev/changelog/discountedunitprice-on-draftorderlineitem-customer-account-api-deprecation
Shopify Customer Account API: remove Customer.lastIncompleteCheckout selections (Checkout subtree removed in 2026-10)
Pack shopify-customer-last-incomplete-checkout:
- Delete lastIncompleteCheckout selections (including the nested Checkout selection block) from Customer Account API GraphQL queries
Upstream changelog: https://shopify.dev/changelog/customer-account-api-customer-lastincompletecheckout-and-checkout-types-removed
Shopify Admin API: drop deprecated isCumulative argument on marketingEngagementCreate
Pack shopify-marketing-engagement-cumulative:
- Remove the deprecated cumulative flag from marketingEngagementCreate calls (GraphQL field, variable declaration, and JS variables object)
Upstream changelog: https://shopify.dev/changelog/deprecation-of-cumulative-marketing-engagements
Shopify Admin API: handle new OrderDisplayFulfillmentStatus value FULFILLMENT_NOT_REQUIRED (2026-10)
Pack shopify-order-fulfillment-not-required:
- Insert case 'FULFILLMENT_NOT_REQUIRED' as a fall-through before case 'UNFULFILLED' in switches over displayFulfillmentStatus
- Insert a FULFILLMENT_NOT_REQUIRED entry mirroring the UNFULFILLED entry in status-to-value object maps
Upstream changelog: https://shopify.dev/changelog/orderdisplayfulfillmentstatus-now-returns-fulfillment_not_required
Preview each migration as one diff
Every pack runs locally as a dry run by default. On the bundled fixture repos each pack rewrites the affected GraphQL documents and call sites, writing a unified patch you can read before anything is applied:
npx mendapi scan # -> which files touch the affected Shopify API surfaces, with line numbers mendapi fix --migration shopify-customer-account-draftorder-price # -> dry run: changes.patch + fix-report.json, nothing applied mendapi fix --migration shopify-customer-account-draftorder-price --apply # -> applies the reviewed diff
Nothing leaves your machine at any step; zero network calls is the default and only mode. The report records which rule fired where, and every rewritten file carries a syntax-check verdict, so review is a read of the patch, not an audit of the tool.
What the codemods refuse to touch
A migration tool earns trust by what it leaves alone. GraphQL rewrites here anchor on the affected API's document shape, not on bare identifiers: a discountedUnitPrice field on an unrelated object is never renamed, and the fulfillment-status pack only inserts the new FULFILLMENT_NOT_REQUIRED case where an exhaustive switch or status map over displayFulfillmentStatus already exists — it does not invent handling logic. Every rule is locked by golden-fixture regression tests with negative controls (fixture lines that must survive byte-for-byte), so a rule that starts over-firing fails our build before it reaches your repo.
The part no codemod fixes
Honest scope: the packs handle the mechanical rewrites. They do not decide what your checkout flow should do now that the Checkout subtree is gone, whether your analytics should recompute historical marketing engagements as non-cumulative, or how mTLS certificates get provisioned for the card deposit endpoint. The tracked Shopify history counts 18 breaking or deprecation entries, and the platform-behavior ones stay not code-fixable by verdict — the Shopify tracker lists every verdict with its reason — because pretending a codemod covers them is how migrations go wrong.
Related
- Shopify API breaking changes: complete tracker — every tracked entry with fixability verdicts.
- PayPal Server SDK migration — the same codemod discipline applied to back-to-back breaking majors.
- Migration pack catalog — every deterministic fix mendapi ships.