2.0 Beta

Smootify 2.0 is in public beta. Your 1.x store keeps working. Move when you want.

What changed

Shopify AJAX API

The Shopify theme endpoints Smootify answers on an Astro site (/cart.js, /cart/add.js, /products/{handle}.js and more), what each one supports, how to start app code after Smootify, and how to turn the emulation off.

v2.0 beta
Open .md
On this page
  1. How it works
  2. The endpoints
  3. Start the app’s code after Smootify
  4. App proxy paths
  5. Turn it off
  6. Where people get stuck

Apps written for Shopify themes call the theme’s AJAX endpoints, such as /cart.js or /cart/add.js. An Astro site has no Shopify theme, so Smootify answers these calls on the page itself, from Shopify’s Storefront API, with the same response shape a theme gets. The cart they change is the page’s cart. This page lists what is answered and how to make an app’s code work with it.

How it works

  • What is intercepted. fetch and XMLHttpRequest calls to the site’s own address, and to the store’s .myshopify.com address, whose path is one of the endpoints below. Every other request goes out unchanged.
  • Same cart as the page. A change through /cart/add.js, /cart/update.js (or their .json forms), /cart/change.js or /cart/clear.js goes through Smootify’s cart: the page’s cart updates as if the visitor had used it, and smootify:cart_updated goes out (with smootify:added_to_cart for an addition).
  • Filters apply. Lines added with /cart/add.js go through the add_to_cart_lines filters, with no element in the context.
  • The request address. As on Shopify, each answer carries the address of the request: response.url for fetch (its clones too), responseURL for XMLHttpRequest. Some app snippets tell an addition from a read by it.
  • window.Shopify. Smootify also sets Shopify.shop, Shopify.locale, Shopify.country, Shopify.routes.root and Shopify.theme ("Smootify"), which theme scripts read.
  • Shopify.actions.updateCart. The cart action of Shopify’s storefront web components, such as the Shopify Inbox chat. Smootify defines window.Shopify.actions.updateCart(payload) unless the page already has one: lines in the Storefront API shape (merchandiseId adds a line, the id of a /cart.json item key sets its quantity, 0 removes it), discountCodes replaces the codes, note sets the note. It answers { cart, userErrors }, with the cart in the Storefront API shape and line ids it accepts back. Smootify also writes Shopify’s cart cookie, which these components read.

The endpoints

EndpointStatusNotes
GET /cart.js, /cart.jsonansweredthe page’s cart
POST /cart/add.js, /cart/add, /cart/add.jsonansweredform data or JSON, items[], properties, selling_plan; sections ignored
POST /cart/update.js, /cart/update.jsonansweredupdates by id, key or position, note, attributes
POST /cart/change.jsansweredid or line, quantity, properties
POST /cart/clear.jsanswered
GET /products/{handle}.js, .jsonansweredfrom Smootify’s cache when it has the product
GET /products.jsonfirst pagelimit up to 250 (default 30); page above 1 answers an empty list
GET /collections/{handle}/products.jsonfirst pagelimit up to 250 (default 50)
GET /recommendations/products.jsonansweredproduct_id, limit up to 10, intent related (default) or complementary
GET /variants/{id}.jsonanswered{ product_variant }, as the Online Store answers
GET /search/suggest.jsonansweredq, resources[type], resources[limit] up to 10: products, collections, pages, articles, queries
POST /localizationansweredcountry_code, language_code change the market; no redirect
GET /cart/shipping_rates.json, POST /cart/prepare_shipping_rates.jsonnot supported422, with Shopify’s error shape: shipping rates exist only at checkout
/apps/*, /a/*, /community/*, /tools/*not answeredsee below

The same list is in the Reference: AJAX API.

products.json and collections/{handle}/products.json answer only their first page, because the Storefront API pages through results in another way.

Start the app’s code after Smootify

Smootify loads as an asynchronous module. A plain script at the end of the page runs before it: its /cart.js calls reach your site’s server, which answers 404, and a snippet that wraps window.fetch there never sees /cart/add.js. Two ways to wait:

  • Change the snippet’s type to smootify-load. Smootify runs it right after smootify:loaded. Add data-type="module" if it was a module; its other attributes are kept. In an .astro file, add is:inline to the tag so that Astro leaves it as it is.
  • Start it from a smootify:loaded listener registered in an is:inline script in the head of your layout, before Smootify finishes loading. The event fires once per page: code added later, by a tag manager for example, does not see it.
<script is:inline type="smootify-load">
  fetch("/cart/add.js", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ items: [{ id: 44012345678901, quantity: 1 }] }),
  }).then((response) => response.json());
</script>

For adding an app’s snippet to a page, see Shopify apps.

App proxy paths

Some Shopify apps call their own server through the store, on paths such as /apps/…, /a/…, /community/… or /tools/…. These app proxies need Shopify’s Online Store. Smootify does not answer them: the calls reach the network as they are, and your site answers 404. On the dev server and on a *.workers.dev address, the debugger names the calls.

Two exceptions keep working: Shopify Inbox’s /apps/ba-rev gets an empty answer, and the consent request to /api/unstable/graphql.json goes to the store.

An app that needs its proxy does not work on an Astro site.

Turn it off

When another script on the page answers these endpoints, turn Smootify’s emulation off in the options:

smootify({ siteId, userOptions: { ajaxApi: false } });

Put it in astro.config.mjs, in the options of the Smootify integration.

Where people get stuck

Check these first

  • An app gets a 404 for /cart.js. Its code ran before Smootify. Change its script type to smootify-load.
  • An app that wraps fetch never sees additions. It wrapped fetch before Smootify did. Start it with smootify-load.
  • An app calls /apps/… and fails. App proxy paths need Shopify’s Online Store and are not answered.
  • A product list from products.json stops after the first page. Only the first page is answered. Raise limit, up to 250.
  • Shipping rates never load. Shipping rates exist only at checkout: the endpoints answer 422.

    ↑ ↓ to moveEnter to openEsc to close