How I stumbled on an unexpected use case for the Shopify AJAX API

Last week, my client messaged me about a feature he wanted for his business’ online store.

He’s a craftsman and he’s set up his store to feature the products he’s made, whether they happen to be in stock currently or not.

However, he didn’t like how Shopify was labeling the items with no inventory as “Out of Stock” all over the store.

He wanted to include the customers in the conversation a bit more, so they could easily be prompted to reach out to him if they were interested in a product that happened not to have inventory at the moment.

The idea was to replace the “Out of stock” copy in the store with something like “Contact us”, and then instead of having a disabled product form button on the PDP, replacing the product form itself with a link to the contact form, with a little note explaining what to do.

Using query strings and JavaScript, I was able to add product data to the contact page, to basically acknowledge “we understand you’re interested in this particular model. If you want this product, or one like it, reach out to us and we’ll get back to you with an estimate.” That was great, but it was frustratingly limited.

For one thing, the string manipulation of the product handle, which was the only data about the product I was passing through the query string to the contact page, was annoyingly complex to get it to look like a proper product title.

I wanted to also include an image of the product, just to further reinforce the continuity of their path to this contact form. I wanted customers to feel seen and heard and for their interest in the specific product to be recognized, even though they are being funneled through this more general contact funnel. After all, it felt like that level of empathy and customer engagement was at the heart of the idea for this feature in the first place.

Enter the AJAX API

The Shopify AJAX API is an unauthenticated REST API. It is often used to programmatically update cart data in theme development. In eCommerce circles, you’ll often hear about the AJAX cart, a side cart found on most stores where you can update the shopping cart without visiting the /cart URL or reloading the page. That functionality uses this API.

Want to ping it? Simply visit your favorite Shopify store in your browser, and if it’s using the traditional theme architecture (in other words, it’s not a headless build), you can visit /products or /collections and tack a .json at the end to see a payload.

Simple and beautiful right? But there’s a catch. I say “a payload” rather than “the payload” for a reason: it’s a truncated dataset. You’ll only ever get a maximum of 30 items in the JSON array, so 30 products, 30 collections, etc.

Based on this lack of completeness in its payloads, I’ve wondered in the past how I could use this API to solve a real world problem, aside from the obvious and taken-for-granted-at-this-point AJAX cart.

However, I recently learned that if you request an endpoint for a particular product, the data you get is still limited, but it is a complete dataset. You won’t find metafields or some other properties of the product Liquid object we have come to love, but that’s more by design that it is hitting a resource limit (like the only showing 30 products is).

You do get the basics, however. Product title, id, description (as rendered HTML), tags, images, variants, are all there. Plenty to customize a contact form page.

How did I use it?

I had originally passed the product handle from the PDP into the href of the conditionally-rendered contact form link as a param of the query string, like so: ‘product=product-handle‘.

Then, on the contact form page, I parsed that query string using the urlSearchParams API and extracted the handle from it.

The code looked like this:

const queryString = window.location.search;
if (queryString === '') {
    console.log('no URL params found');
} else {
    const urlParams = newURLSearchParams(queryString);
    if (urlParams.get('origin') === 'product') {
        const productHandle = urlParams.get('product');
        // call string methods on productHandle 
        // & create html & add it to the DOM
    }
}

(The ‘origin’ query param was just to specify that the user found there way to the page through the PDP. Probably unnecessary.)

So I already had (unwittingly) done most of the legwork. All I had to do was fetch the endpoint, which would be done by interpolating the productHandle like this:

const response = fetch(`https://www.exampleshopifystore.com/products/${productHandle}`;

(Note that this line of code is inside an async function, which lets me later use the await keyword, which is why the AJAX call is so succinct.)

Then it was a matter of getting the data I wanted and adding it to the store. This wound up being pretty much the same process as before, but this time with way more useful data than I could ever in good conscience try to pass through a query string. And no byzantine string manipulation needed.

Simple, quick, and easy. Even with styling, the whole thing took only a few short hours.

Shopify AJAX API, how could I have ever doubted you?