Building a Shopify App Extension: Display Order Count to Boost Product Popularity


In the competitive world of e-commerce, social proof can be a powerful tool to drive sales. One effective way to leverage this is by displaying the number of orders for a product, showcasing its popularity and potentially influencing purchasing decisions. In this tutorial, we’ll walk you through the process of building a Shopify app that displays the order count on product pages. By the end, you’ll have a functional app that not only enhances the shopping experience but also potentially boosts your conversion rates.

*Pre-requisites: ensure you have the Shopify CLI installed, a Shopify Partner Account, and a test store.

Code: https://github.com/ndrishinski/popular-products

Prefer Video?

Enjoy, otherwise read on!

https://youtu.be/26bd5UhZqBk

Scaffold App

The first step is to use the Shopify CLI app scaffolding that gives us a Remix setup out of the box.

  1. Navigate to the directory where you want to create your app. Your app will be created in a new subdirectory.
  2. Then run shopify app init .
  3. When prompted, enter a name for your app, and then select Start with Remix to use the Remix template

Start Local Development Server

Now we can start our development server to get our app up and running.

  1. Navigate to your newly created app directory.
  2. Run the following command: shopify app dev

Per the Shopify documentation, this process:

  • Guides you through logging into your Partner account and selecting a Partner organization
  • Creates an app in the Partner Dashboard, and connects your local code to the app
  • Creates your Prisma SQLite database
  • Creates a tunnel between your local machine and the development store

Install App on Development Store

You can install your app on your development store, and automatically populate your development store with products that you can use for app testing.

  1. With the server running, press p to open your app’s preview URL in a browser. When you open the URL, you’re prompted to install the app on your development store.
  2. Click Install app to install the app on the development store. You now have a development store running with your new app installed.

Create Admin Polaris / App Bridge Embed

Now that we have our local environment up and running, we can start creating our app. The first piece of this app is going to be creating a page in the Shopify admin using Polaris compnents and App Bridge to show our products with the number of orders. To do this let’s create a file in the app/routes directory called app.products.jsx and paste the following code in:

import { useLoaderData } from "@remix-run/react";
import {
  Page,
  Layout,
  Card,
  ResourceList,
  ResourceItem,
  Text,
} from "@shopify/polaris";

import { authenticate } from "../shopify.server";

export async function loader({ request, params }) {
  const { admin } = await authenticate.admin(request);
  const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
  const response = await admin.graphql(
    `#graphql
      query {
        orders(first: 250, query: "created_at:>'${yesterday}'") {
          edges {
            node {
              lineItems(first: 250) {
                edges {
                  node {
                    product {
                      id
                      title
                    }
                    quantity
                  }
                }
              }
            }
          }
        }
      }`
  );

  const responseJson = await response.json();
  const orders = responseJson.data.orders.edges;

  const productOrderCounts = orders.reduce((acc, order) => {
    order.node.lineItems.edges.forEach(lineItem => {
      const product = lineItem.node.product;
      if (product) {
        const productId = product.id;
        if (!acc[productId]) {
          acc[productId] = { title: product.title, count: 0 };
        }
        acc[productId].count += lineItem.node.quantity;
      }
    });
    return acc;
  }, {});
  
  const productsOrdered = Object.entries(productOrderCounts).map(([id, data]) => ({
    id,
    title: data.title,
    orderCount: data.count
  }));
  return {productsOrdered}
}

export default function Products() {
  const { productsOrdered } = useLoaderData<typeof loader>();
  return (
    <Page title="Products">
      <Layout>
        <Layout.Section>
          <Card>
            <ResourceList
              resourceName={{ singular: 'Product', plural: 'Products' }}
              items={productsOrdered}
              renderItem={(order) => (
                <ResourceItem
                  id={order.id}
                  name={order.name}
                  url={`/products/${order.id}`}
                >
                  <div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
                    <Text variant="bodyMd" fontWeight="bold" as="h3">
                      {order.title}
                    </Text>
                    <Text variant="bodyMd" fontWeight="bold" as="h3">
                      {order.orderCount}
                    </Text>
                  </div>
                </ResourceItem>
              )}
            />
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}

Wow that’s a lot of code, what is happening? Well based on the Remix architecture, we created an admin page that is first querying the Admin API via GraphQLfor orders from the previous 24 hours. Then using some JavaScript we run through our orders and create an array in order from the most ordered products to the least. After returning that data from the loader function, we display in nicely in the UI using Polaris compnents.

Now we need to add a link to display our new page. In the routes/app.jsx file, we want to add a link to this new page like so:

...
<Link to="/app" rel="home">
  Home
</Link>
<Link to="/app/products">Product Order Count</Link>
...

Now if we save our code and check out the preview, we should have a link in our app to this new page. Make sure you create some test orders to actually see any data on this page.

Creating API endpoint

Now that we have our admin page built, let’s create an API endpoint that our app extension will use to show in our widget. In the app/routes directory, create a file called api.products.jsx and paste the following code:

import { json } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import { cors } from 'remix-utils';

export const loader = async ({ request }) => {
  const url = new URL(request.url);
  const productId = url.searchParams.get("productId");
  const { admin  } = await authenticate.public.appProxy(request)

  const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();

  const response = await admin.graphql(
    `#graphql
    query {
      orders(first: 200, query: "created_at:>'${twentyFourHoursAgo}' AND product_id:${productId}") {
        edges {
          node {
            id
          }
        }
      }
    }`
  );

  const responseJson = await response.json();
  const orderCount = responseJson.data.orders.edges.length;

  const newResponse = json({
    ok: true,
    message: "Success",
    data: orderCount,
  });

  return cors(request, newResponse);
};

What is happening here? First we grabbing the product ID that will come in the request, and querying the admin API via GraphQL for the orders from the previous 24 hours. We then return this data with an object including the number of orders that we will use to display in the widget.

Grant Data Permissions

Before we can use our newly created API, we need to grant some permissions. In the Partner Dashboard, click into your app and navigate to ‘API access’ and fill out the top section which asks about data use. Once we fill that out and save, we should be in the clear.

Generate a Theme Extension

Now we will create our app theme extension which is the actual widget that will show in the UI.

  1. Ensure you are in the current app directory
  2. Run shopify app generate extension
  3. Select Theme app extension as the extension type.
  4. Provide a name for your extension.

Now if we restart our app server we should have access to our theme extension in the Customizer. But first let’s customize it.

In the extensions/<name-of-app>/blocks directory, remove any existing files and create a file named product-popup.liquid (or whichever name you prefer). Now we can paste the following code:

<div style="min-height: 24px;">
  <p id="order-info"></p>
</div>

<script>
  document.addEventListener('DOMContentLoaded', function() {
    const appUrl = '/apps/popular-product/api/products?productId={{ product.id }}';
    
    fetch(appUrl)
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.json();
    })
    .then(result => {
      if (result.data > 0) {
        const html = `
        <span><span>${result.data}</span> Orders Since Yesterday!</span>
      `
      document.querySelector('#order-info').innerHTML = html
      }
    })
    .catch(error => console.error('Fetch error:', error));
  });
</script>

{% schema %}
{
  "name": "Popular-Product",
  "target": "section",
  "settings": [
    
  ]
}
{% endschema %}

This code should look familiar to theme devs, because it is just a regular liquid file like we see in themes. We have a simple schema, but most importantly a small div element that we will update with the number of orders this product has had in the past 24 hours. Our JavaScript makes the API call to the endpoint we just created and then updates the DOM. Before this all works, we need to create an app proxy so that our API can authenticate this request.

Creating an App Proxy

  1. From your Partner Dashboard, click Apps.
  2. Click the name of your app.
  3. Click Configuration.
  4. Navigate to the App proxy section and click Set up.
  5. In the App proxy section, select a prefix from the Subpath prefix drop-down list.
  6. Enter a sub path in the Subpath field
  7. These settings determine which HTTP requests to Shopify are proxied to the proxy URL entered in the next step. For example, if the sub path prefix is apps, and the sub path is store-pickup, then any path in your online store after https://example.myshopify.com/apps/store-pickup will be proxied to the provided proxy URL.
  8. Enter the URL of your proxy server in the Proxy URL field. This is the URL that will be proxied from the path that you entered in the previous step.
  9. Click Save

Here is what mine looks like:

Add in Theme Customizer

Now that we have added all of our code, we should be able to open the Theme Customizer and add our app block. On your development theme click Customize, and navigate to the Product template. Then we should be able to click to add a block, pick the app name and add accordingly. Note this will not display anything yet because it relies on JavaScript.

Now if we click ‘Preview’ in our theme we should see our widget displaying correctly on any products with 1 or more orders.

Conclusion

Congratulations! You’ve successfully built a Shopify app that displays order counts on product pages. This simple yet effective feature can significantly impact your store’s performance by leveraging social proof. Remember, the key to a successful e-commerce strategy often lies in these small details that enhance user experience and build trust. As you continue to develop and refine your Shopify app, consider other ways you can use data to create compelling shopping experiences that drive sales and customer loyalty.

Leave a Reply

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