<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Josh Boddy</title>
    <description>The latest articles on Forem by Josh Boddy (@joshatflare).</description>
    <link>https://forem.com/joshatflare</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F643709%2F83d5cd20-6875-49c6-b9b6-8eec7b4996db.jpg</url>
      <title>Forem: Josh Boddy</title>
      <link>https://forem.com/joshatflare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/joshatflare"/>
    <language>en</language>
    <item>
      <title>Implementing A/B Testing on your Shopify App with Mixpanel and Split.io</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 10 Apr 2024 13:00:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/implementing-ab-testing-on-your-shopify-app-with-mixpanel-and-splitio-2ind</link>
      <guid>https://forem.com/joshatflare/implementing-ab-testing-on-your-shopify-app-with-mixpanel-and-splitio-2ind</guid>
      <description>&lt;p&gt;One of the keys to any successful app is validation of the idea. When you create an app you want to know if that app is making a difference to your users. You can then use that information to research and discover new ways that your app is not only being interacted with, but also changes and adjustments you can make (small or large) to improve your usability, performance and enhance the experience for the brands using your app. This is where A/B testing (split testing) comes in. It's more focussed on the side of validation of ideas, but is equally important in discovering how your app can improve based on the way it's being used currently alongside demonstrating the proof of your app’s value to your customers&lt;/p&gt;

&lt;p&gt;In this post we'll be talking about how we've implemented A/B testing, how we use it to track conversion rates with our application, and how you can do the same or utilise the same tools to track different metrics for your own applications. Conversion rates are important in the case of any app utilising App Blocks or Embeds, as we need to ensure we are not decreasing the number of sales that a customer store makes, or even demonstrate that we are improving that number of sales as well. We'll also be briefly touching upon how we can then action these metrics to make data driven decisions in changing an application to improve the user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Toolbox
&lt;/h2&gt;

&lt;p&gt;The title of this post mentions two tools: &lt;a href="https://www.mixpanel.com/"&gt;Mixpanel&lt;/a&gt; and &lt;a href="https://split.io/"&gt;Split.io&lt;/a&gt;. Let's go over the roles that they will play in our split testing setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mixpanel - This allows us to collect "events" on the frontend of our application as well as to pass properties to those events to fine tune the data we collect and the state of our app as users interact with it. These events could occur on the click of an element (i.e. a button or link) and the data supplied could be the current product or variant ID, or even the items they have in their basket, etc. We feed these into a dashboard that we then use as a reference to see how / how often different parts of the application are interacted with.&lt;/li&gt;
&lt;li&gt;Split.io - This SDK allows us to direct traffic selectively. In this case we may want to show 50% of users the page with the application and 50% of users the page without the application present. We can then infer metrics from the way that these users interact with the page in combination with Mixpanel to draw conclusions on how effectively our app is increasing, decreasing or maintaining certain interactions with a page. We can show that our apps decrease, increase or maintain conversion rates on variants for instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By routing traffic selectively to pages with Split.io and then tracking how that traffic interacts with the pages they see, we can gain full insight into how a user is leveraging our app or how our app is influencing the user flow. Therefore we are capable of understanding the full user journey and the role our app plays in that flow to either positive or negative effect, thus allowing us to make data driven adjustments to improve a specific target metric or improve the overall experience for our users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's code!
&lt;/h2&gt;

&lt;p&gt;For our implementation, we should follow the user journey to simplify the implementation for ourselves. So let's start with Split.io. As we mentioned, Split.io is the first thing that loads in our app to check which group a user belongs to (group A or group B, where one sees the application and one doesn't), so let's look at how to load Split.io within our app.&lt;/p&gt;

&lt;p&gt;We first need to download the &lt;code&gt;split.min.js&lt;/code&gt; file from the Split.io website and put it in our assets directory. We can then use a bit of JS magic to load the Split library in asynchronously as to not affect load times and only when it is specifically needed. We do this using a &lt;code&gt;loadSplit&lt;/code&gt; function shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function loadSplit() {
  return new Promise(resolve =&amp;gt; {
    const flareContent = document.getElementById("flare-add-to-cart");
    const script = document.createElement("script");
    script.src = "{{ "split.min.js" | asset_url }}";
    script.onload = () =&amp;gt; {
      resolve(splitio);
    }
    flareContent.appendChild(script);
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can call this function inline with a &lt;code&gt;.then()&lt;/code&gt; following to run code once the library is loaded&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loadSplit().then(() =&amp;gt; {
// Split Testing Code
})

// Alternatively use the async/await syntax

async function myFunc() {
  const splitio = await loadSplit();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then initialise a Factory inside of Split.io and retrieve a client instance&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loadSplit().then((splitio) =&amp;gt; {
  var factory = splitio.SplitFactory({
    authorizationKey: "&amp;lt;YOUR_AUTH_KEY&amp;gt;",
    key: "A Random Key for Each User"
  });

  var client = factory.client();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can implement the split. We need to create feature flags for our stores in Split.io to match with our store name so we can track traffic dependent on the store that our app is currently tracking on. We use a bit of regex to grab the shop name from the URL and pass it to our client. The first function we need is the &lt;code&gt;extractShopName&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function extractShopName(url) {
// Regular expression to match the shop name at the start of the URL, before '.myshopify.com'
    const pattern = /^(.+?)\.myshopify\.com$/;
    const match = url.match(pattern);

    if (match) {
      return match[1];// The first capturing group contains the shop name
    } else {
      return null;// Return null if the URL does not match the expected format
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should sit above our Split.io code so we can call it within the &lt;code&gt;loadSplit&lt;/code&gt; function. We then grab our client treatment via the SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loadSplit().then((splitio) =&amp;gt; {
  var factory = splitio.SplitFactory({
    authorizationKey: "&amp;lt;YOUR_AUTH_KEY&amp;gt;",
    key: "A Random Key for Each User"
  });

  var client = factory.client();

///////////////////////////////////////////////

  client.on(client.Event.SDK_READY, function() {
    var treatment = client.getTreatment(extractShopName(product.shop));
    if (treatment == "app_hidden") {
// The code here runs for users who aren't shown the app
    } else if (treatment == "app_shown") {
// The code here runs for users who are shown the app
    } else {
// Something funky is happening
    }
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which completes our Split.io portion of code for the app.&lt;/p&gt;

&lt;p&gt;Now let's look at Mixpanel. We have a split of users, some looking at our app and some not. We want to see what they do. The beauty of Mixpanel is that we can track the same events for each of these users, but separate the users at this Split stage to ensure we're collecting the same data for users who see the app and users that don't. In any case we need to start with enabling the Mixpanel SDK. We can do this using the Mixpanel CDN as Mixpanel has to be loaded on page load (unlike Split.io). We can do this with the following in our app block liquid code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script type="text/javascript"&amp;gt;
// Optional custom proxy route to stop ad blockers
// const MIXPANEL_PROXY_DOMAIN = '&amp;lt;YOUR_CUSTOM_PROXY_URL&amp;gt;';
  const MIXPANEL_CUSTOM_LIB_URL = MIXPANEL_PROXY_DOMAIN ? MIXPANEL_PROXY_DOMAIN + "/lib.min.js" : undefined;
  (function (f, b) {
    if (!b.__SV) {
      var e, g, i, h;
      window.mixpanel = b;
      b._i = [];
      b.init = function (e, f, c) {
        function g(a, d) {
          var b = d.split('.');
          2 == b.length &amp;amp;&amp;amp; ((a = a[b[0]]), (d = b[1]));
          a[d] = function () {
            a.push([d].concat(Array.prototype.slice.call(arguments, 0)));
          };
        }
        var a = b;
        'undefined' !== typeof c ? (a = b[c] = []) : (c = 'mixpanel');
        a.people = a.people || [];
        a.toString = function (a) {
          var d = 'mixpanel';
          'mixpanel' !== c &amp;amp;&amp;amp; (d += '.' + c);
          a || (d += ' (stub)');
          return d;
        };
        a.people.toString = function () {
          return a.toString(1) + '.people (stub)';
        };
        i =
          'disable time_event track track_pageview track_links track_forms track_with_groups add_group set_group remove_group register register_once alias unregister identify name_tag set_config reset opt_in_tracking opt_out_tracking has_opted_in_tracking has_opted_out_tracking clear_opt_in_out_tracking start_batch_senders people.set people.set_once people.unset people.increment people.append people.union people.track_charge people.clear_charges people.delete_user people.remove'.split(
            ' '
          );
        for (h = 0; h &amp;lt; i.length; h++) g(a, i[h]);
        var j = 'set set_once union unset remove delete'.split(' ');
        a.get_group = function () {
          function b(c) {
            d[c] = function () {
              call2_args = arguments;
              call2 = [c].concat(Array.prototype.slice.call(call2_args, 0));
              a.push([e, call2]);
            };
          }
          for (var d = {}, e = ['get_group'].concat(Array.prototype.slice.call(arguments, 0)), c = 0; c &amp;lt; j.length; c++)
            b(j[c]);
          return d;
        };
        b._i.push([e, f, c]);
      };
      b.__SV = 1.2;
      e = f.createElement('script');
      e.type = 'text/javascript';
      e.async = !0;
      e.src =
        'undefined' !== typeof MIXPANEL_CUSTOM_LIB_URL
          ? MIXPANEL_CUSTOM_LIB_URL
          : 'file:' === f.location.protocol &amp;amp;&amp;amp; '//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js'.match(/^\/\//)
          ? 'https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js'
          : '//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js';
      g = f.getElementsByTagName('script')[0];
      g.parentNode.insertBefore(e, g);
    }
  })(document, window.mixpanel || []);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We won't touch on custom proxies for Mixpanel but by setting one up and uncommenting the first line in the script tag, you can leverage a proxy to gather data that might be blocked by an Ad blocker (lots of Mixpanel URLs are).&lt;/p&gt;

&lt;p&gt;We now have access to the Mixpanel library in our scripts so long as the above is added in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag. We can then implement it in the Split.io loader with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loadSplit().then((splitio) =&amp;gt; {
  var factory = splitio.SplitFactory({
    authorizationKey: "&amp;lt;YOUR_SPLITIO_AUTH_KEY&amp;gt;",
    key: "A Random Key for Each User"
  });

  var client = factory.client();

///////////////////////////////////////////////

  mixpanel.init("&amp;lt;YOUR_MIXPANEL_AUTH_KEY&amp;gt;", { api_host: "&amp;lt;YOUR_MIXPANEL_PROXY_DOMAIN&amp;gt; or &amp;lt;OTHER&amp;gt;", debug: true, persistence: "localStorage" });

///////////////////////////////////////////////

  client.on(client.Event.SDK_READY, function() {
    var treatment = client.getTreatment(extractShopName(product.shop));
    if (treatment == "app_hidden") {
// The code here runs for users who aren't shown the app
    } else if (treatment == "app_shown") {
// The code here runs for users who are shown the app
    } else {
// Something funky is happening
    }
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've initialised Mixpanel, so now let's add our first event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;loadSplit().then((splitio) =&amp;gt; {
  var factory = splitio.SplitFactory({
    authorizationKey: "&amp;lt;YOUR_SPLITIO_AUTH_KEY&amp;gt;",
    key: "A Random Key for Each User"
  });

  var client = factory.client();

  mixpanel.init("&amp;lt;YOUR_MIXPANEL_AUTH_KEY&amp;gt;", { api_host: "&amp;lt;YOUR_MIXPANEL_PROXY_DOMAIN&amp;gt; or &amp;lt;OTHER&amp;gt;", debug: true, persistence: "localStorage" });

  client.on(client.Event.SDK_READY, function() {
    var treatment = client.getTreatment(extractShopName(product.shop));
    if (treatment == "app_hidden") {
// The code here runs for users who aren't shown the app
      mixpanel.track(
        "App Page Viewed",
        {
          app_shown: false
        }
      )
    } else if (treatment == "app_shown") {
// The code here runs for users who are shown the app
      mixpanel.track(
        "App Page Viewed",
        {
          app_shown: true
        }
      )
    } else {
// Something funky is happening
    }
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We should now see events appear in the Mixpanel interface. We are officially tracking and split testing! We can also conditionally render the app based on these if statements with a simple &lt;code&gt;document.getElementById("my-app-container").remove()&lt;/code&gt; in the &lt;code&gt;treatment == "app_hidden"&lt;/code&gt; check result.&lt;/p&gt;

&lt;p&gt;From here onwards we can use the Mixpanel SDK to track other events for users interacting with the page. Let's say we have a button in our App Block that we want to track:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button onclick="runEventAndTrack()"&amp;gt;My App Button&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function would look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function runEventAndTrack() {
  console.log("Button Clicked!");
  mixpanel.track("My App Button Clicked");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will tie in with the initial event we tracked within our &lt;code&gt;loadSplit&lt;/code&gt; callback to understand whether or not a user who is or isn't seeing the app has clicked the app button (a user who isn't seeing the app shouldn't be able to click the button, great for debugging!). This also can be tied into event listeners on existing elements on the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Get the Add to Cart Button from the Page
const addToCartButton = document.getElementById("add-to-cart-button");
addToCartButton.addEventListener("click", () =&amp;gt; {
  mixpanel.track("Add to Cart Button Clicked", {
    variant_id: "&amp;lt;VARIANT_ID&amp;gt;"
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will allow us to check whether or not users who have or haven't seen the app have added the current variant to the cart. As such we can  deduce whether or not our app is increasing or decreasing conversion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metrics are Cool
&lt;/h2&gt;

&lt;p&gt;Hopefully this gives you a good starting point to implement split testing on your own applications. data driven decisions are important because you can't make assumptions about what your customers want, the likelihood is you won't meet the largest market you can if you don't rely on your customers for feedback. By leveraging data and tuning your app appropriately based on that data you can ensure that your customers get the highest value out of your application and you will continue to expand your list of them.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;/p&gt;

&lt;p&gt;~ Josh&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>split</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Leveraging AI and GitHub Copilot in Shopify App Development</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 03 Apr 2024 13:00:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/leveraging-ai-and-github-copilot-in-shopify-app-development-140h</link>
      <guid>https://forem.com/joshatflare/leveraging-ai-and-github-copilot-in-shopify-app-development-140h</guid>
      <description>&lt;p&gt;AI is very quickly becoming an integral part of the development experience. We’re seeing new innovations every day and each one finds a new way to shake up the programming space irreversibly. ChatGPT and Copilot are the main culprits and since their respective launches, most developers know when they’re beat and try to leverage these tools to write code not only more efficiently, but also much faster. You can use these tools to 10X your development flow within any context but if you know Flare, then you know we’re about to show you how to use it to write quick and effective Shopify Applications.&lt;/p&gt;

&lt;p&gt;With tools like Devin being released, slowly developers are becoming less and less necessary for the development of commercial applications as a whole. Learning to use these tools effectively as they appear will ensure career longevity in a world where programming skills are becoming less valuable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Copilot is a paid service from GitHub, you will have to pay a monthly fee for the service if you have already used your free trial. ChatGPT is free up to a point, however, in order to use their newest models, you will have to pay a fee for that as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What can AI do for me?
&lt;/h2&gt;

&lt;p&gt;This is where all developers start, the real question is “if I’m a good enough developer, do I really need to use AI tools?” the simple answer is yes. The more complex answer is that AI does what we humans do (it trained off the back of what we’ve achieved in the last 50 or so years of computing) and leverages the ability to access all that knowledge and apply it context. Think of AI as a seasoned, 70 year old developer standing over your desk and telling you exactly what to type… but without the frustrated and disappointed scowls. GitHub Copilot’s code generation inline will help with contextual development on a small scale, you can quickly write a comment for a function that you can’t be bothered to write and copilot will go ahead and do the heavy lifting. Alternatively if you want a larger snippet then head to ChatGPT (or a GPT alternative like Bard) and watch the magic as you describe intricate functionality and it supplies you with a well formatted article-esque response containing code files you can just copy and paste to build your project. If you think something is going to take a long time, try a GPT model first and give yourself at the very least a starting point to get going from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The drawbacks
&lt;/h2&gt;

&lt;p&gt;As much as it’s nice having a nice seasoned developer on your shoulder to guide you through your project / bug fix, ChatGPT and Copilot are definitely not flawless and have a few quirks that you need to understand and get to know before being able to leverage them to their fullest extent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code clarity is key
&lt;/h3&gt;

&lt;p&gt;This one is specifically for Copilot. It’s a really useful tool but struggles a lot with messy codebases. It takes the code that you’ve already written and tries to intelligently suggest auto-completions to help make the development experience easier, but as is always the problem with machines, it struggles to manage human error and messiness. If it can’t read your code and figure out what you’re trying to do, then it can’t help you do things. It’s simple. You’ve got to imagine showing your code to a senior developer and asking for help. If they can’t read your code how can they help you? They can’t. This means that code clarity and good organisation, as well as clarity in naming variables and functions are important to ensuring Copilot suggestions are relevant and helpful.&lt;/p&gt;

&lt;h3&gt;
  
  
  That’s so 2010…
&lt;/h3&gt;

&lt;p&gt;ChatGPT and Copilot are trained on historical data. As in, the data on the internet up until September 2021 (Jan 2022 in newer models) but still outdated all the same. Should you choose to use Agents on ChatGPT you can have them scrape custom documents and the internet for more info, but it will always be outdated just slightly, and as such cannot always be relied upon entirely. Take for instance Shopify’s Polaris components. The team behind Polaris are constantly updating the library and as such you can run into components that are deprecated or even removed outright when asking a GPT to generate code with the Polaris component library, because it’s referencing a version of Polaris that is no longer the latest version. As such you have to maintain a bit of caution or just feed in the newest version of the docs as text into ChatGPT but that becomes more tedious that just writing the code yourself. However, you can get around this by getting it to generate scripts using the older library version and feeding in the relevant pieces of info to help update it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t be too reliant
&lt;/h3&gt;

&lt;p&gt;While these tools are all great they make for very bad programmers. If you rely too heavily on ChatGPT or Copilot and it generates a piece of code that you don’t understand or can’t understand, then you need to tread with care. If that piece of code either throws an error or generates a logic error then you are going to spend even longer cleaning up those repercussions than you would’ve spent writing that code from scratch. Make sure that when you generate code you understand what each line is doing in detail in order to rectify any mistakes that it could make down the line. You can ask Copilot or your GPT model to explain the code it gives you in order to help increase your understanding of what’s going on to avoid this situation entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage in Shopify App Development
&lt;/h2&gt;

&lt;p&gt;All of the points above hold strong in any development environment, but we’re here to talk about Shopify App development, so let’s do that. Within your app you can apply AI tools to a few different areas and in a few different ways to acquire an incredible amount of power in your projects. Here are a few places that I see these tools really shine for Shopify App development in particular.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL Response Structure Inference
&lt;/h3&gt;

&lt;p&gt;GraphQL is powerful but a little confusing to work with for some. Especially if you are used to non-document based DB interactions like SQL. Copilot is incredibly useful for understanding the structure of responses from GraphQL queries if formatted correctly. Supply a GraphQL string query such as the one below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GET_ORDER_FULFILLMENT_ID_QUERY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    query getOrderFulfillmentId($id: ID!) {
        order(id: $id) {
            displayFulfillmentStatus
            fulfillmentOrders(first: 50) {
                edges {
                    node {
                        id
                        status
                        deliveryMethod {
                            methodType
                        }
                        lineItems(first: 100) {
                          edges {
                            node {
                              id
                              sku
                              totalQuantity
                            }
                          }
                        }
                    }
                }
            }
        }
    }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Queries like this can be quite difficult to read, understand and handle the response of. With Copilot, once this query is called it can infer the response of the query as well as generate code to handle errors, helping deal with those long nested objects that allow you to access the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repeated calls to a Database or GraphQL
&lt;/h3&gt;

&lt;p&gt;If you wish to repeat a similar query with some slight adjustments then Copilot can very easily substitute those values for you through what I’m calling a “smart, enriched copy and paste” where in it will infer the value you wish to retrieve or modify and adjust the snippet you’ve already written just slightly to address the correct location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setShopPlanActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UpdateCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shopifyPaymentPlans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;UpdateExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;set planStatus = :s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACTIVE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;ReturnValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ALL_NEW&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above function runs an update command on a DynamoDB table to change the value of a shop’s payment plan to active. We can simply write the function name for &lt;code&gt;setShopPlanInactive&lt;/code&gt; and watch as Copilot takes the function above and adjusts all the relevant values to generate the rest of the function without us having to manually sift through and change that data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setShopPlanInactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UpdateCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shopifyPaymentPlans&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;shop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;UpdateExpression&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;set planStatus = :s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ExpressionAttributeValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;:s&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INACTIVE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;ReturnValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ALL_NEW&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dbClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of my favourite features of Copilot as boilerplate code is always a hassle to repeat, and this means you can focus more time on the interesting problem solving of programming rather than re-writing the same function but slightly differently.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL Query Generation with Efficiency
&lt;/h3&gt;

&lt;p&gt;Because GraphQL is a bit of a complex query structure language, it can sometimes be hard to tell if you are pulling the optimal amount of data with the least number of queries. We can leverage a GPT model to help us generate GraphQL queries (watch out for the API version) and allow it to do the heavy lifting in terms of query structure for us. This is as simple as asking your model to generate a query that pulls in all orders and their respective users from the last 12 months, and watching it spit out a nice GQL structure for you to copy and paste. The power of converting human readable language to code should never be underestimated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vanilla JS in seconds
&lt;/h3&gt;

&lt;p&gt;We’ve talked before about leveraging Vanilla JS in your app blocks and app embeds to enhance your app’s functionality, but sadly not many people are used to using Vanilla JS anymore as frameworks like React and Svelte have become increasingly popular. As such, you may find yourself not knowing how to implement some functionality in your frontend. Copilot and GPT models can do this work for you as well, however be cautious that it doesn’t know the code will be injected in the page with the app block. You can use this to generate functions and efficient code snippets to implement frontend functionality without the hassle of having to relearn what a DOM is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Leveraging AI is not without it’s risks and caveats, but if understood and used correctly it can 10X your development workflow and skyrocket productivity. These tools can be applied to any area of your Shopify Apps (frontend, backend, App Bridge, etc.) and when paired with a consistent and tidy codebase can be used to do half the development work for you. Make sure you understand the risks before implementing but spending time on this new wave of tech is definitely not a waste.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;/p&gt;

&lt;p&gt;~ Josh&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>ai</category>
      <category>chatgpt</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Using Theme UI Extensions in your Shopify App</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 27 Mar 2024 14:30:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/using-theme-ui-extensions-in-your-shopify-app-4k</link>
      <guid>https://forem.com/joshatflare/using-theme-ui-extensions-in-your-shopify-app-4k</guid>
      <description>&lt;p&gt;Shopify provides several methods to enhance online storefronts, ranging from ScriptTags for JavaScript injection to modern solutions like App Embeds and App Blocks. These advanced options enable seamless integration of custom app UIs into online stores, reducing or eliminating the need for retailers to modify their theme liquid code. Despite some limitations, understanding and utilising App Embeds and App Blocks allows developers to craft compelling and unique experiences for both shoppers and retailers through Shopify Apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Exploring App Embeds and App Blocks&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Theme UI Extensions are built on App Embeds and App Blocks, created using &lt;strong&gt;&lt;code&gt;npm run shopify app extension generate&lt;/code&gt;&lt;/strong&gt;. This command generates a folder within your extension containing assets, blocks, snippets, and locales essential for your extension. The blocks folder is particularly noteworthy, as it contains App Blocks, liquid code files that can be effortlessly added to the Online Store through the Theme Editor. By properly configuring these blocks, retailers can seamlessly incorporate your app's features into their storefront.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Blocks&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;App Blocks, situated in the blocks folder, are crafted for easy integration. Retailers can add these blocks to their store through the Theme Editor, thereby incorporating your app's functionalities into their site. A typical App Block schema includes elements like name, target area on the page, associated stylesheet, conditions for availability, and customisable settings for a personalised user experience.&lt;/p&gt;

&lt;p&gt;Example schema for an App Block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% schema %}
  {
    "name": "My App Block",
    "target": "section",
    "stylesheet": "my_app_block.css",
    "available_if": "{{ app.metafields.app.appBlockEnabled }}",
    "settings": [
      {
        "type": "header",
        "content": "My App Block Settings"
      }, {
        "type": "product",
        "id": "product",
        "label": "product",
        "autofill": true
      }, {
        "id": "background_color",
        "type": "color",
        "default": "#000000",
        "label": "Background Color"
      }
    ]
  }
{% endschema %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Embeds&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Contrasting with Blocks, App Embeds do not directly include HTML and CSS. They leverage JavaScript to inject functionality across all pages of an online storefront, extending the app's reach beyond specific pages.&lt;/p&gt;

&lt;p&gt;Example schema for an App Embed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% schema %}
  {
    "name": "App Embed",
    "target": "body",
    "stylesheet": "app_embed.css",
    "javascript": "app_embed.js",
    "settings": [
      {
        "type": "number",
        "id": "app_embed_number",
        "label": "App Embed Number",
        "default": 2
      }
    ]
  }
{% endschema %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Snippets&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Snippets enable liquid code modularization, promoting reusability and maintainability. They can contain HTML, CSS, or JavaScript, and are defined without a schema.&lt;/p&gt;

&lt;p&gt;Example snippet usage in an App Block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="my_app_block"&amp;gt;
  {{ render "my_snippet", background_color: block.settings.background_color }}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in &lt;strong&gt;&lt;code&gt;my_snippet.liquid&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="my_snippet" style="background: {{ background_color }}"&amp;gt;
  &amp;lt;h1&amp;gt;Hello App Block with Snippet&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Assets and Locales&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Assets house files like JavaScript, CSS, and images, essential for your app. Locales contain translation files, allowing your app to serve a global audience.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Addressing Limitations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;While offering significant benefits, Theme UI extensions have limitations, such as placement restrictions for App Blocks, compatibility with older themes, and challenges in incorporating liquid data within JavaScript and CSS files in the assets directory. Creative problem-solving can effectively navigate these issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;App Embeds and App Blocks are invaluable for enriching Shopify storefronts with customised app functionalities. Despite some challenges, their adept use can result in immersive user experiences. Developers are encouraged to leverage these tools, alongside CSS and JavaScript assets, to deliver tailored and locale-sensitive solutions.&lt;/p&gt;

&lt;p&gt;Happy Coding,&lt;/p&gt;

&lt;p&gt;Josh&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>frontend</category>
      <category>liquid</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to use external libraries in Theme App Extensions for your Shopify App</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 13 Mar 2024 15:30:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/how-to-use-external-libraries-in-theme-app-extensions-for-your-shopify-app-5922</link>
      <guid>https://forem.com/joshatflare/how-to-use-external-libraries-in-theme-app-extensions-for-your-shopify-app-5922</guid>
      <description>&lt;p&gt;One of the joys of programming is leveraging existing solutions rather than reinventing the wheel. When someone has already developed a library that meets your needs, there's no need to write it from scratch. This principle is especially relevant within the Shopify ecosystem, where tools like Polaris for App Bridge components and the Shopify API for GraphQL and REST endpoints are invaluable. However, when developing frontend Theme UI extensions, the approach becomes more nuanced.&lt;/p&gt;

&lt;p&gt;Shopify takes great care to ensure Theme UI extensions do not adversely affect the storefront experience. Excessive page load times can reduce conversion rates and interactions, potentially costing store owners sales. Consequently, Shopify imposes restrictions on remote access and the methods used to access content, given the significant impact of request-reliant content on page load times. Despite these limitations, many libraries offer compelling functionalities. The challenge lies in balancing performance with added value.&lt;/p&gt;

&lt;p&gt;Numerous libraries enable the incorporation of unique components into your application without the need to write complex code for implementation. While there are countless examples, let's use FlatPickr to illustrate the process of integrating an external library into your Theme UI extensions and accessing it within your liquid code.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Adding Libraries&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Shopify restricts remote imports from CDNs, leading to CORS errors and deployment issues if attempted. While sometimes these restrictions are mere suggestions, other times they are strict prohibitions due to loading concerns. However, there is a workaround. To minimize load times, consider adding minified JS or CSS files to the Shopify CDN for your app.&lt;/p&gt;

&lt;p&gt;Upon creating a Theme UI extension, you'll notice an &lt;strong&gt;&lt;code&gt;assets&lt;/code&gt;&lt;/strong&gt; folder in your extension directory. This folder can contain HTML, CSS, JS, images, and other assets, which you can then incorporate into your code. When deployed, these assets receive a Shopify CDN URL. However, directly calling these assets in your liquid or JS code won't work due to the cloud storage structure. Shopify provides a liquid solution, &lt;strong&gt;&lt;code&gt;asset_url&lt;/code&gt;&lt;/strong&gt;, to access an asset's URL based on its local path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{{ 'flatpickr.min.js' | asset_url }}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet, assuming &lt;strong&gt;&lt;code&gt;FlatPickr.min.js&lt;/code&gt;&lt;/strong&gt; is located at the root of the &lt;strong&gt;&lt;code&gt;assets&lt;/code&gt;&lt;/strong&gt; folder (not in a subdirectory), demonstrates how to embed a script in a page using liquid within a &lt;strong&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;/strong&gt; tag. The &lt;strong&gt;&lt;code&gt;asset_url&lt;/code&gt;&lt;/strong&gt; tag dynamically generates the asset's CDN URL, which is then assigned to the script's &lt;strong&gt;&lt;code&gt;src&lt;/code&gt;&lt;/strong&gt; attribute.&lt;/p&gt;

&lt;p&gt;To embed the script or stylesheet on a page, consider the following approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadFlatPickr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{{ 'flatpickr.min.js' | asset_url }}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flatpickr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;link&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stylesheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{{ 'flatpickr.min.css' | asset_url }}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;embedLocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;link&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;embedLocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function serves as a wrapper to fetch and embed scripts on the page, resolving the promise with the library object once loaded. This ensures the library is accessible only after successful embedding, preventing errors in its usage.&lt;/p&gt;

&lt;p&gt;To use the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;loadFlatPickr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flatpickr&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Use your library here&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// or&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flatpickr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadFlatPickr&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// The library is also accessible in this scope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Integrating external libraries into Shopify can be challenging due to restrictions on remote CDN usage and limitations on the Shopify CDN. The method described above offers a solution that saves time and leverages the vast world of libraries to enhance your UI and streamline development.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;/p&gt;

&lt;p&gt;~ Josh&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>javascript</category>
      <category>libraries</category>
      <category>cdn</category>
    </item>
    <item>
      <title>Running GraphQL Queries and Mutations with no direct session access in your Shopify App</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 06 Mar 2024 14:00:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/running-graphql-queries-and-mutations-with-no-direct-session-access-in-your-shopify-app-3h96</link>
      <guid>https://forem.com/joshatflare/running-graphql-queries-and-mutations-with-no-direct-session-access-in-your-shopify-app-3h96</guid>
      <description>&lt;p&gt;&lt;strong&gt;Running GraphQL Queries and Mutations Without Direct Session Access in Your Shopify App&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GraphQL has become the primary tool for Flare, almost entirely replacing REST APIs. We've discovered that the query/mutation structure significantly reduces the number of API calls we need to make. It also enhances speed and support, particularly with Shopify's GraphQL (GQL) interactions. While versatile and powerful, GQL can sometimes appear daunting and complex to developers.&lt;/p&gt;

&lt;p&gt;This post aims to demystify GQL, making it more accessible, and to address common challenges that can impede progress, especially for newcomers. We'll explore using GQL within a Node.js Express app and discuss accessing sessions for shops externally. This enables asynchronous calls on a store's behalf, allowing for queries and mutations without direct requests from the store.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding Queries and Mutations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you're new to GraphQL, the terms 'queries' and 'mutations' might seem confusing. Here's a simple explanation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Queries&lt;/strong&gt;: Retrieving data from GraphQL in a specific structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutations&lt;/strong&gt;: Modifying data at a remote source, like Shopify's database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For those familiar with REST APIs, think of queries as GET requests to fetch data, and mutations as POST, PUT, DELETE requests to modify data. GQL simplifies this by not requiring the specification of request types for mutations.&lt;/p&gt;

&lt;p&gt;GQL allows for the definition of data return structures through object relationships, which is more complex in REST APIs due to the need for detailed entity relationship definitions.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query productVariant($id: ID!) {
  productVariant(id: $id) {
    inventoryQuantity
    displayName
    product {
      id
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query fetches product variants and their parent products' IDs from Shopify, showcasing GQL's efficiency in retrieving related data in a single request.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Accessing GraphQL&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There are several ways to access GQL in Shopify apps. One handy tool is the GQL editor available at &lt;strong&gt;&lt;code&gt;http://localhost:3457/graphiql&lt;/code&gt;&lt;/strong&gt; during development. This web interface allows for testing GQL queries against your development store's data.&lt;/p&gt;

&lt;p&gt;Incorporating GQL into your app involves setting up authenticated sessions, crucial for query and mutation operations. Shopify's authentication process ensures secure client creation for GQL requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use("/api/*", shopify.validateAuthenticatedSession());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's vital to ensure middleware compatibility and maintain the &lt;strong&gt;&lt;code&gt;validateAuthenticatedSession()&lt;/code&gt;&lt;/strong&gt; function as provided.&lt;/p&gt;

&lt;p&gt;Creating a GQL client within an app route might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const GET_PRODUCT_VARIANT_QUERY = `
  query productVariant($id: ID!) {
    productVariant(id: $id) {
      inventoryQuantity
      displayName
      product {
        id
      }
    }
  }
`

app.get("/api/productVariant", async (req, res) =&amp;gt; {
  res.setHeader("Access-Control-Allow-Origin", "*");
  const client = new shopify.api.clients.Graphql({
    session: res.locals.shopify.session,
  });
  const { body } = await client.query({
    data: {
      query: GET_PRODUCT_VARIANT_QUERY,
      variables: {
        id: req.query.id
      }
    }
  });
  if (body.data.productVariant) {
    res.status(200).json(body.data.productVariant);
  } else {
    res.status(500).json({ error: true });
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example demonstrates defining a GQL query, setting up a route for the query, creating a GQL client, and handling the query response.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Running GraphQL Without an Authenticated Session&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A common challenge is executing GQL operations without an active session. For tasks like automated emails or inventory updates, an authenticated client is necessary even without a direct user request.&lt;/p&gt;

&lt;p&gt;The solution lies in accessing session storage with the shop's URL, allowing for authenticated client creation without an active session in the request object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const sessionId = shopify.api.session.getOfflineId(shop);
  const session = await shopify.config.sessionStorage.loadSession(sessionId);
  const client = new shopify.api.clients.Graphql({
    session,
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach enables authenticated GQL operations anywhere in the app, such as within webhook handlers.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This overview should clarify the advantages of GraphQL over REST, demonstrate practical GQL usage in Shopify app development, and provide strategies for overcoming common obstacles like session management. We encourage sharing further insights and tips to help the developer community leverage GraphQL's full potential.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;/p&gt;

&lt;p&gt;~ Josh&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>shopify</category>
      <category>graphql</category>
      <category>programming</category>
    </item>
    <item>
      <title>Managing and Handling Configurations in a Shopify App</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 28 Feb 2024 14:00:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/managing-and-handling-configurations-in-your-shopify-app-3l07</link>
      <guid>https://forem.com/joshatflare/managing-and-handling-configurations-in-your-shopify-app-3l07</guid>
      <description>&lt;p&gt;Many Shopify App developers have likely encountered several &lt;code&gt;.toml&lt;/code&gt; files scattered across their workspaces. These files are found not only in the root folder of your app but also within any extensions you might have generated. What exactly are these files? I'm delighted you're curious.&lt;/p&gt;

&lt;p&gt;TOML, or Tom's Obvious Minimal Language files, contain data that can be parsed and utilised by developers. Think of them as a minimal database holding essential information your app requires to function, such as your app's name, access key, and all the URLs your app needs to connect to.&lt;/p&gt;

&lt;p&gt;Managing these files might seem overwhelming, but they're a valuable asset in app development, aiding in various tasks like deployment.&lt;/p&gt;

&lt;p&gt;If you've used &lt;code&gt;npm run deploy&lt;/code&gt;, you might have also noticed a &lt;code&gt;.env&lt;/code&gt; file. We've previously discussed environment variables (stored within &lt;code&gt;.env&lt;/code&gt; files), but today we'll delve deeper into managing your app's critical data efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding TOML Files
&lt;/h2&gt;

&lt;p&gt;Consider TOML files as "Shopify Owned." The data within these files is often sent to Shopify's servers. If you check your Partners Dashboard under "Configuration," you'll find much of the same information that's in your TOML file. This is because they're interconnected.&lt;/p&gt;

&lt;p&gt;When you execute &lt;code&gt;npm run dev&lt;/code&gt; or &lt;code&gt;npm run deploy&lt;/code&gt;, Shopify extracts data from your TOML file, ensuring it matches their records. This process highlights the importance of your TOML file as the source of truth for your app, dictating how Shopify deploys and accesses your app and the permissions it has.&lt;/p&gt;

&lt;p&gt;It's wise to maintain separate TOML files for development and production to avoid any mix-ups. You can easily switch between these files using the &lt;code&gt;npm run shopify app config use &amp;lt;TOML&amp;gt;&lt;/code&gt; command, substituting &lt;code&gt;&amp;lt;TOML&amp;gt;&lt;/code&gt; with the desired file's name.&lt;/p&gt;

&lt;p&gt;TOML files include crucial information like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scopes&lt;/strong&gt;: Permissions your app requests from stores. Adding a scope means updating your TOML file and deploying, but be aware that some scopes might require Shopify's approval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoints&lt;/strong&gt;: URLs in the file should link back to your app, with development files pointing to your tunnel endpoint and production files to your live URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Version&lt;/strong&gt;: This specifies the API version your requests target. Use the most up-to-date version or "unstable" for Developer Preview features, albeit with caution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client ID&lt;/strong&gt;: This identifies your app to Shopify, so ensure it matches the one in your Partners Dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To mitigate errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pay attention to the deployment logs, which display your app's name and connected store. This helps catch any unintended connections to production.&lt;/li&gt;
&lt;li&gt;Use the Partners Dashboard to correct any misconfigurations quickly.&lt;/li&gt;
&lt;li&gt;If a development TOML file mistakenly makes it into production, a subsequent commit with the correct production TOML should resolve the issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite their complexity, TOML files become manageable with understanding and careful practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Role of &lt;code&gt;.env&lt;/code&gt; Files
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.env&lt;/code&gt; files store environment variables, which are crucial for your app's functioning without exposing sensitive data. They're prevalent across development projects, offering a secure way to store essential variables.&lt;/p&gt;

&lt;p&gt;You can define environment variables in a &lt;code&gt;.env&lt;/code&gt; file without specifying types, ensuring proper escaping for non-standard values. These variables become accessible in your project via the &lt;code&gt;dotenv&lt;/code&gt; package, allowing you to reference them as &lt;code&gt;process.env.VARIABLE_NAME&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Remember, never commit your &lt;code&gt;.env&lt;/code&gt; files to your repository to prevent sensitive data exposure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Deployment and &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.gitignore&lt;/code&gt; file is your safeguard against committing sensitive files to your repository. By listing TOML and &lt;code&gt;.env&lt;/code&gt; files here, you prevent them from being accidentally included in your commits, streamlining your workflow and enhancing security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;TOML and &lt;code&gt;.env&lt;/code&gt; files are fundamental in configuring your Shopify app, facilitating secure communication and data handling with Shopify. While they may initially seem intimidating, a clear understanding and careful management will allow you to deploy your app efficiently and securely, accessing a wealth of information safely.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;/p&gt;

&lt;p&gt;~ Josh&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>config</category>
    </item>
    <item>
      <title>Using Deep Links Effectively in Your Shopify App Theme Extensions</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 21 Feb 2024 14:00:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/using-deep-links-effectively-in-your-shopify-app-theme-ui-extensions-33m9</link>
      <guid>https://forem.com/joshatflare/using-deep-links-effectively-in-your-shopify-app-theme-ui-extensions-33m9</guid>
      <description>&lt;p&gt;Theme extensions are a fantastic way to let store customers engage with your app. They enable the use of App Blocks to seamlessly integrate your custom Liquid, HTML, JavaScript, and CSS code onto Shopify 2.0 storefronts, empowering customers to enjoy your app's features on most store pages (checkout pages being the exception). As an app developer, it's wise to leverage Deep Links to simplify the embedding of app blocks for your users. However, these links can seem a tad complex at first. Our goal today is to demystify Deep Links, making them more approachable and manageable for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Link Structure
&lt;/h2&gt;

&lt;p&gt;You can find the basic structure for Deep Links in &lt;a href="https://shopify.dev/docs/apps/online-store/theme-app-extensions/extensions-framework#deep-linking"&gt;Shopify's Developer Documentation&lt;/a&gt;, but let's break it down further, focusing on key areas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;myshopifyDomain&amp;gt;/admin/themes/current/editor?template=${template}&amp;amp;addAppBlockId={uuid}/{handle}&amp;amp;target=newAppsSection

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the standard format for a Deep Link. Let's dissect it, shall we?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;myshopifyDomain&amp;gt;&lt;/code&gt;: This is your &lt;code&gt;.myshopify.com&lt;/code&gt; URL, not to be confused with your shop domain. Using a custom URL here might lead to errors.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{template}&lt;/code&gt;: Refers to a page template in your Theme Editor. You can add an App Block to any page with a JSON template in your code. The &lt;code&gt;product&lt;/code&gt; page is a prime example.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{uuid}&lt;/code&gt;: This represents the &lt;code&gt;SHOPIFY_EXTENSION_NAME_ID&lt;/code&gt; environment variable from your project's &lt;code&gt;.env&lt;/code&gt; file. We'll delve into how to fetch and utilise these variables in your codebase shortly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{handle}&lt;/code&gt;: The name of your app block's liquid file. Simply omit the &lt;code&gt;.liquid&lt;/code&gt; extension when using it here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{header|footer|aside}&lt;/code&gt; &lt;strong&gt;optional&lt;/strong&gt;: This specifies the section group for your app block. You can choose from the options provided or leave this out entirely.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{sectionID}&lt;/code&gt;: The ID of the section where you want to place your app block. You'll find this in the theme's JSON templates, listed under &lt;code&gt;sections&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we've got the basics down, let's explore some clever ways to streamline this process and save you some hassle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the Fuss?
&lt;/h2&gt;

&lt;p&gt;Knowing what a Deep Link &lt;em&gt;should&lt;/em&gt; look like is one thing, but actually using one in your app dashboard is another kettle of fish. Where do these environment variables come from? And what on earth is a Section ID, and how do you find it?&lt;/p&gt;

&lt;p&gt;Many of these values are dynamic, varying between stores and installations. This presents a challenge for us developers, as we need to relay data from the backend to the frontend securely (keeping environment variables off the frontend), ensuring all values are accurate and functional. Here are a few strategies to tackle this...&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Our backend can serve as a powerhouse, providing an endpoint for the frontend to query and fetch the desired app block details. Let's start with the basics of string concatenation. Store the original template URL in a constant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const deepLinkUrl = "https://&amp;lt;myshopifyDomain&amp;gt;/admin/themes/current/editor?template=${template}&amp;amp;addAppBlockId={uuid}/{handle}&amp;amp;target=newAppsSection";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, fill in the blanks with known values. For instance, if we're adding our app block to the product page, we'd set the template as &lt;code&gt;product&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const deepLinkUrl = "https://&amp;lt;myshopifyDomain&amp;gt;/admin/themes/current/editor?template=product&amp;amp;addAppBlockId={uuid}/{handle}&amp;amp;target=newAppsSection";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, wrap this in an endpoint to glean details from the current user session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get("/api/deeplinks", (req, res, next) =&amp;gt; {
  res.setHeader("Access-Control-Allow-Origin", "*");
  const deepLinkUrl = "https://&amp;lt;myshopifyDomain&amp;gt;/admin/themes/current/editor?template=product&amp;amp;addAppBlockId={uuid}/{handle}&amp;amp;target=newAppsSection";

  res.status(200).send(deepLinkUrl);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With access to the user's session info (like the current shop), we can use JavaScript's &lt;code&gt;${value}&lt;/code&gt; syntax for concatenation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get("/api/deeplinks", (req, res, next) =&amp;gt; {
  res.setHeader("Access-Control-Allow-Origin", "*");
  const deepLinkUrl = `https://${res.locals.shopify.session.shop}/admin/themes/current/editor?template=product&amp;amp;addAppBlockId={uuid}/{handle}&amp;amp;target=newAppsSection`;

  res.status(200).send(deepLinkUrl);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding &lt;code&gt;${res.locals.shopify.session.shop}&lt;/code&gt; to the URL dynamically fills in the shop's URL making the request.&lt;/p&gt;

&lt;p&gt;Next, introduce the &lt;code&gt;dotenv&lt;/code&gt; package to your Shopify app with &lt;code&gt;npm install dotenv --save&lt;/code&gt;. Import it at the top of your file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to access those crucial environment variables with &lt;code&gt;process.env.VARIABLE_NAME&lt;/code&gt;, adhering to the best practice of uppercase variable names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";

app.get("/api/deeplinks", (req, res, next) =&amp;gt; {
  res.setHeader("Access-Control-Allow-Origin", "*");
  const deepLinkUrl = `https://${res.locals.shopify.session.shop}/admin/themes/current/editor?template=product&amp;amp;addAppBlockId=${process.env.SHOPIFY_EXT_NAME_ID}/{handle}&amp;amp;target=newAppsSection`;

  res.status(200).send(deepLinkUrl);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After deploying your app (using &lt;code&gt;npm run deploy&lt;/code&gt;), update the environment variable name in your code to match the one in your newly created &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Finally, replace the &lt;code&gt;{handle}&lt;/code&gt; placeholder with your &lt;code&gt;.liquid&lt;/code&gt; file's name to complete your Deep Link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";

app.get("/api/deeplinks", (req, res, next) =&amp;gt; {
  res.setHeader("Access-Control-Allow-Origin", "*");
  const deepLinkUrl = `https://${res.locals.shopify.session.shop}/admin/themes/current/editor?template=product&amp;amp;addAppBlockId=${process.env.SHOPIFY_EXT_NAME_ID}/APP_BLOCK_LIQUID_FILE_NAME&amp;amp;target=newAppsSection`;

  res.status(200).send(deepLinkUrl);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voilà! You've crafted a straightforward endpoint to fetch your Deep Link from the backend, fully populated with all necessary data. But how do you put this to use?&lt;/p&gt;

&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;In your frontend, make a request to your backend to retrieve this data, perhaps using &lt;code&gt;fetch&lt;/code&gt; in a React template or a &lt;code&gt;loader&lt;/code&gt; function in Remix. Then, embed this URL in a &lt;code&gt;Button&lt;/code&gt; component. Clicking this button will effortlessly embed your app block onto a store, bypassing the need for intricate configurations or data manipulation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going Further
&lt;/h2&gt;

&lt;p&gt;Feeling adventurous? Here's a bonus challenge: Create a list of deep link URLs for each app block you develop.&lt;/p&gt;

&lt;p&gt;Consider using Node's &lt;code&gt;fs&lt;/code&gt; library to scan your &lt;code&gt;blocks&lt;/code&gt; folder within the theme app extension directory. Generate a new URL for each file, returning a list in a JSON response to the frontend. Use the &lt;code&gt;.map&lt;/code&gt; function to craft a button for each URL, allowing you to add all your app blocks to the UI as you create them, rather than manually crafting a new button and request for each new block. But that's a story for another day. Let me know how you fare and don't hesitate to reach out with questions.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;p&gt;- Josh&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Using Shell Scripting to simplify your Shopify App development workflow 🐚</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 14 Feb 2024 14:00:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/using-shell-scripting-to-simplify-your-development-workflow-2de2</link>
      <guid>https://forem.com/joshatflare/using-shell-scripting-to-simplify-your-development-workflow-2de2</guid>
      <description>&lt;p&gt;Shopify app development is an incredibly fun job, and Shopify's CLI is very well maintained and updated. However, like with any development task, there are a few areas in which the usage of the CLI can be a bit tedious or even daunting. There is always the fear of accidentally making changes to your app config and not being able to revert them, or even just the hassle of constantly having to startup your Ngrok tunnel and your dev server side by side, to then realise that you only had to make one small change. It's a lot of time lost!&lt;/p&gt;

&lt;p&gt;As developers, our job is to automate the tedious (in most cases to the point where the automation took longer than doing the actual task), but fret not! I have constructed a set of aliases and tools that you can use to simplify your entire workflow without having to dabble in the precarious art of shell scripting. Hopefully saving you plenty of time to focus on the vision and not the boring setup and CLI work that is necessary to bring it to life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Our first requirement is that you have to be on a UNIX (MacOS or Linux) based system. PowerShell users can still follow along with the logic in these aliases and scripts, however they will not be able to use them verbatim on a Windows system (sorry!). We use these scripts in office to simplify our flow and we want to share them with you, so anyone who can convert these to PowerShell tools and could share them would be greatly appreciated!&lt;/p&gt;

&lt;p&gt;Once you have your Mac or Linux machine ready, make sure to downlaod and install &lt;a href="https://github.com/tmux/tmux/wiki"&gt;TMUX (Terminal Mulitplexer)&lt;/a&gt;. A lot of our scripts are going to be running headless inside of a TMUX session as it's an incredibly clean way to manage and organise different workspaces simultaneously. A lot of our scripts will help us to interact with TMUX so don't worry if it looks a little intimidating at first. You can install TMUX using your package manager in the terminal, use whichever applies to you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MacOS&lt;/strong&gt; - &lt;code&gt;brew install tmux&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ubuntu&lt;/strong&gt; - &lt;code&gt;sudo apt-get install tmux&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arch&lt;/strong&gt; - &lt;code&gt;pacman -S tmux&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;There are a ton more package manager options, if yours isn't listed then check out your package manager to find the right way to install TMUX on your system, or refer to the &lt;a href="https://github.com/tmux/tmux/wiki/Installing"&gt;TMUX wiki&lt;/a&gt; to find install instructions for your system.&lt;/p&gt;

&lt;p&gt;Finally, you want to locate a &lt;code&gt;.rc&lt;/code&gt; file for your specific shell. In my case, I have a &lt;code&gt;.zshrc&lt;/code&gt; file that I will be using, yours might be a &lt;code&gt;.bashrc&lt;/code&gt; file, but if you don't have either of those look for your specific shell using the &lt;code&gt;echo "$SHELL"&lt;/code&gt; command to find it, then look up where your &lt;code&gt;.rc&lt;/code&gt; file is located and what it is named. In some cases you might not have one, in which case you may have to explore how to create Shell Aliases in your own environment (not covered). A &lt;code&gt;.rc&lt;/code&gt; file is essentially a small script that runs whenever you open up your terminal (or start your shell) that defines any customisations you want to make to your shell for while you're using it, this means any changes we make to it are propagated through any terminal that we may open in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shell Aliases
&lt;/h2&gt;

&lt;p&gt;Shell Aliases are a neat way to shorten long and complicated sequences of commands into quick one-liners (often one word) to simplify your experience of using the terminal. We spoke briefly at the end of the last section about your &lt;code&gt;.rc&lt;/code&gt; file, this is where we will be adding these Aliases into in order to get them running and recognised on any terminal that we open.&lt;/p&gt;

&lt;p&gt;Open your &lt;code&gt;.rc&lt;/code&gt; file in your text editor of choice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quick Tip - Trying to do this in your file explorer is quite difficult, as any files with a . at the start are hidden by default. You should probably use your terminal and the ls -a command to view them, alongside a terminal based editor like Nano or Vim to edit the contents.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can pick and choose the aliases you would like to use from below and copy and paste them into your &lt;code&gt;.rc&lt;/code&gt; file to add them to your terminal environment.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tunnel&lt;/code&gt; can be used to run your Ngrok tunnel mentioned in the &lt;a href="https://dev.to/joshatflare/set-up-a-team-environment-for-shopify-app-development-4eng"&gt;previous article&lt;/a&gt; with the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias tunnel="ngrok http --domain=YOURTUNNEL 4000"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to change &lt;code&gt;YOURTUNNEL&lt;/code&gt; to whatever the domain of your tunnel is, and change the port number &lt;code&gt;4000&lt;/code&gt; to whatever port you will be running your server on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;startdev&lt;/code&gt; can be used to navigate to your project directory and run the &lt;code&gt;./run_dev.sh&lt;/code&gt; script we'll talk about shortly&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;startdev() {
  cd /YOUR/PROJECT/ROUTE/HERE
  ./run_dev.sh
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure again to change the &lt;code&gt;/YOUR/PROJECT/ROUTE/HERE&lt;/code&gt; value to the direct path to your project (not a relative path, or this won't work from anywhere you may be in your terminal).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;checkdev&lt;/code&gt; can be used to quickly check on the status of your development TMUX server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;checkdev() {
  tmux a -t SERVERNAME
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SERVERNAME&lt;/code&gt; value here can be whatever you want, but make sure it aligns with the name you use in the &lt;code&gt;run_dev.sh&lt;/code&gt; script we will discuss later. This will attach you to the headless TMUX session so you can see both Ngrok and your development server running side by side. You can quickly exit out of this session by pressing &lt;code&gt;Ctrl+B&lt;/code&gt; and then &lt;code&gt;D&lt;/code&gt; to leave the server running in the background.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;deploy&lt;/code&gt; can be used to deploy your application version to Shopify on your production TOML file or your development TOML file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deploy() {
  cd /YOUR/PROJECT/ROUTE/HERE

  case $1 in
    "dev")
      npm run shopify app config use PRODCONFIGNAME &amp;amp;&amp;amp; npm run deploy
      ;;
    "prod")
      npm run shopify app config use DEVELOPMENTCONFIGNAME &amp;amp;&amp;amp; npm run deploy
      ;;
    *)
      echo "Unknown Environment"
      ;;
  esac
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The values &lt;code&gt;PRODCONFIGNAME&lt;/code&gt; and &lt;code&gt;DEVELOPMENTCONFIGNAME&lt;/code&gt; refer to the names of your TOML files, which you can read more about managing on the &lt;a href="https://shopify.dev/docs/apps/tools/cli/managing-app-configuration-files"&gt;Shopify.dev&lt;/a&gt; site.&lt;/p&gt;

&lt;p&gt;This will conditionally check whether or not you want to run a production deployment or a development deployment based on the parameters you pass to the alias. It's usage could be &lt;code&gt;deploy prod&lt;/code&gt; to deploy your production environment or &lt;code&gt;deploy dev&lt;/code&gt; to deploy your development environment. If you use any other paramter it will return &lt;code&gt;Unknown Environment&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quick Tip - Once you've saved your .rc file the changes won't show until you either close and then reopen your terminal, or if you run the source ~/.RCFILE command  (in my case it's source ~/.zshrc) to reset the contents of the file in the current terminal window.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Scripts
&lt;/h2&gt;

&lt;p&gt;We've mentioned scripts already, all of them rely on using tmux as a detached source but you can tweak them to run just in your terminal environment. They are a nice way to localise the above Aliases into your projects as to make sure that you don't get confused running the wrong commands inside of the wrong project (particularly in the case that you're developing multiple apps at once). Scripts also allow us to chain commands more effectively and pass in a lot more data, but it's all essentially the same programming language.&lt;/p&gt;

&lt;h3&gt;
  
  
  run_dev.sh
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

tmux new-session -d -s SERVERNAME 'ngrok http --domain=YOURTUNNEL 4000 &amp;amp;&amp;amp; exit';
tmux rename-window ngrok;
tmux split-window;
tmux send 'npm run dev -- --tunnel-url=YOURTUNNEL:4000 --theme THEMEID --store STOREPREFIX &amp;amp;&amp;amp; exit' ENTER;
tmux rename-window SERVERNAME;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SERVERNAME&lt;/code&gt; is the name you want to give your TMUX server (can be anything)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;YOURTUNNEL&lt;/code&gt; is the domain name of the Ngrok tunnel you created in the previous article&lt;/p&gt;

&lt;p&gt;&lt;code&gt;THEMEID&lt;/code&gt; can be found by entering your development store's online theme editor and copying the digits inside of the URL&lt;/p&gt;

&lt;p&gt;&lt;code&gt;STOREPREFIX&lt;/code&gt; is the prefix of the URL to your development store (i.e. the prefix for flare-dev.myshopify.com would be flare-dev)&lt;/p&gt;

&lt;p&gt;This script starts a tmux session that contains both Ngrok and your dev server. Make sure to replace the values with your specific config values. &lt;code&gt;SERVERNAME&lt;/code&gt; should be equivalent in both your &lt;code&gt;checkdev&lt;/code&gt; alias and the script above. This should sit in the root of your folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  check_dev.sh
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# !/bin/bash

tmux a -t SERVERNAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may be thinking "this looks very similar to the &lt;code&gt;checkdev&lt;/code&gt; alias we talked about earlier" and you'd be right! It's exactly the same but this lives inside of your project directory to make sure you're connecting to the right TMUX server for the right project. This is beneficial for lack of confusion, but also helps me demonstrate that a lot of these are very similar and can be cross applied in either a local script inside of your project, or as a terminal-wide shortcut. We encourage you to play around and set up a flow that's right for you, using these scripts as a guide to get you started on the journey to productivity mastery inside of your Shopify App.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;Shell scripting can be a bit scary because of the power that it has (that &lt;code&gt;sudo rm -rf /*&lt;/code&gt; is terrifying) but it also holds a lot of potential good ways to simplify our lives and help us to focus more on what we are passionate about. In our case, it's the development of Shopify Apps, wherein Shell Scripting helps us hone in on the app development rather than the admin of running servers and deployments. I encourage you to go and research Shell Scripting in it's entirety to find even more clever and creative ways to implement it into not just your Shopify App but any other programming project you may pursue.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>productivity</category>
      <category>tutorial</category>
      <category>shopify</category>
    </item>
    <item>
      <title>Set up a Team Environment for Shopify App Development</title>
      <dc:creator>Josh Boddy</dc:creator>
      <pubDate>Wed, 07 Feb 2024 14:00:00 +0000</pubDate>
      <link>https://forem.com/joshatflare/set-up-a-team-environment-for-shopify-app-development-4eng</link>
      <guid>https://forem.com/joshatflare/set-up-a-team-environment-for-shopify-app-development-4eng</guid>
      <description>&lt;p&gt;Let's say you've just come up with a cool idea for a Shopify App with a friend. You've done some planning, have an idea of how you want to implement your app, and then the moment you create your repository you bump into one of a few issues that means you can't work on it together. Your app is a huge project, you don't want to have to work on it by yourself. &lt;/p&gt;

&lt;p&gt;You do some research and you see some people talk about Git and version control online but you can't actually test your code simultaneously. It's a tricky situation but have no fear. At Flare, we’ve been building a Shopify app and after a bit of trial and error, we've managed to come up with a few workarounds to some common issues you might face when attempting to set up a team development environment for a new Shopify App.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are using the &lt;a href="https://github.com/Shopify/shopify-app-template-node" rel="noopener noreferrer"&gt;Node JS Starter&lt;/a&gt; for this project, however the concepts are transferrable to other frameworks as well!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tunnels (&lt;a href="https://www.cloudflare.com/en-gb/products/tunnel/" rel="noopener noreferrer"&gt;CloudFlare&lt;/a&gt; vs. &lt;a href="https://ngrok.com" rel="noopener noreferrer"&gt;Ngrok&lt;/a&gt;)
&lt;/h2&gt;

&lt;p&gt;The big one... Shopify provide a tunnelling service to expose your app to the internet. This is because when you open your app, it's visible as an iFrame inside of Shopify's app pages. The iFrame then renders whatever is in your App Bridge directory, i.e your frontend React (or whatever other) interface you use. The Shopify app page needs to be able to see your code in your local codebase, and as such Shopify use a default tunnel generated in CloudFlare. This is a recent switch as they used to use Ngrok, however CloudFlare tunnels are dynamically allocatable and there's no hassle in the form of setting up an account or creating domains like there is in Ngrok.&lt;/p&gt;

&lt;p&gt;That being said, Ngrok has a few things going for it that made it our choice to use as a tunnel provider, these include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live traffic monitoring either in your terminal / console, or on their web dashboard&lt;/li&gt;
&lt;li&gt;Simulating and repeating network requests through the tunnel&lt;/li&gt;
&lt;li&gt;Visibility into live tunnels running and the ability to remotely shut them down from the admin page&lt;/li&gt;
&lt;li&gt;TLS/SSL certificates on those tunnel domains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and a few more, but I'll spare you the details. What I won't spare you is a few tips and tricks to get going with Ngrok instead of Shopify's default CloudFlare.&lt;/p&gt;

&lt;p&gt;First and foremost, go to &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;Ngrok's Website&lt;/a&gt; and sign up for an account. Then follow the installation instructions to set up Ngrok in your terminal. Ensure that you and your teammate set up different accounts and different tunnels. You will each want to go to the 'Domains' tab in the left hand panel and create a domain. You get one free static domain per user account without upgrading, this should be free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0f73rjhh2giop8fm6yw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0f73rjhh2giop8fm6yw.png" alt="Ngrok Dashboard Domains page after creation of your tunnel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once created, click on the small terminal icon to the right of your Ngrok domain and copy the command to start your tunnel on your local machine. Make sure you're passing the port of your Shopify App server through there as well, it should look something like &lt;code&gt;ngrok http --domain=YOURNGROKDOMAINHERE 4000&lt;/code&gt; in the case that your app port is 4000. Run this in a terminal and you should see a nice Ngrok window appear. Leave this running the background and open a new window and navigate into the root of your Shopify app project.&lt;/p&gt;

&lt;p&gt;You can pass your own flags to the &lt;code&gt;npm run dev&lt;/code&gt; command by adding an extra &lt;code&gt;--&lt;/code&gt; before you add your flags, in this case we want to add the &lt;code&gt;--tunnel-url=&lt;/code&gt; flag to our command to specify the domain of our Ngrok tunnel, in which case we use &lt;code&gt;npm run dev -- --tunnel-url=YOURNGROKDOMAINHERE&lt;/code&gt; to force our dev server to run on the tunnel domain that we specify and not generate a random CloudFlare tunnel.&lt;/p&gt;

&lt;p&gt;Once we're running both of those commands together, we should be able to access our app on our Ngrok domain.&lt;/p&gt;

&lt;p&gt;You might be saying "well now I can connect to my app but my teammate can't run his code at the same time, this didn't help at all..." hold your horses! We've got an app service running but the most important part is where you point it. Read to learn about Git and most importantly, development stores!&lt;/p&gt;

&lt;h2&gt;
  
  
  Git and Version Control
&lt;/h2&gt;

&lt;p&gt;Now you may have heard of Git and Version Control as one of the key tools in a programmers' tool belt, well those people are wrong... this &lt;em&gt;is&lt;/em&gt; the tool belt. A programmer that doesn't use Git is a programmer ready and willing to lose everything to an accidental misclick, a corrupted disk or a cat falling asleep on the backspace key.&lt;/p&gt;

&lt;p&gt;Version Control is pretty self explanatory, it is essentially a periodic save of whatever changes you've made to your code. A Git user can regularly make &lt;em&gt;commits&lt;/em&gt; to a repository in order to save their code changes. If they should change something that breaks the code, rather than trying to make it work or changing things to try and get it back the way it was, the programmer can just revert back to the previous commit. Think of it as a backup of your code, a time machine.&lt;/p&gt;

&lt;p&gt;So let's talk about branches. This is the bit we really want to focus on today. Branches allow us to create separate sets of changes from our code that branch off into different versions. In this case, we are going to cleverly create a &lt;code&gt;development&lt;/code&gt; branch and a &lt;code&gt;main&lt;/code&gt; branch to act as our development and production environments respectively.&lt;/p&gt;

&lt;p&gt;We do this simply by running &lt;code&gt;git checkout -b development&lt;/code&gt; in the root folder of our Shopify App. This will switch us to a &lt;code&gt;development&lt;/code&gt; branch. We can make changes in here and when we are happy to move them into &lt;code&gt;main&lt;/code&gt; we can create what is called a &lt;em&gt;pull request&lt;/em&gt; to merge the changes in &lt;code&gt;development&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;. This can be done easily in a remote Git Repository hosted on a platform like &lt;a href="https://www.github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://www.bitbucket.org" rel="noopener noreferrer"&gt;BitBucket&lt;/a&gt;. Creation of Pull Requests (here on referred to as PRs) differs between each of these platforms in the interface, but the concept is still the same, make sure to look up creating a PR in your specific remote repository provider and ensure you are merging &lt;code&gt;development&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt; and not vice versa.&lt;/p&gt;

&lt;p&gt;An optional extra that is common amongst large scale companies is to create &lt;em&gt;feature branches&lt;/em&gt; which is just a fancy way of saying anytime you want to fix a bug, add a new feature or make any goal based changes, you create a new branch in your repository. In this case, &lt;code&gt;development&lt;/code&gt; would be our base branch so we would create a new branch from &lt;code&gt;development&lt;/code&gt; named after your feature (i.e. &lt;code&gt;added-styling-to-div&lt;/code&gt;) that you would then merge into &lt;code&gt;development&lt;/code&gt; and then eventually into &lt;code&gt;main&lt;/code&gt; to deploy it to your end user. Think of the &lt;code&gt;development&lt;/code&gt; branch as the place that holds all of your upcoming features while you test them and make sure they work, and then &lt;code&gt;main&lt;/code&gt; as the version of your app that your user sees.&lt;/p&gt;

&lt;p&gt;In a later article, we will go over how we automatically deploy our app from the &lt;code&gt;main&lt;/code&gt; branch using GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development Stores
&lt;/h2&gt;

&lt;p&gt;The crucial finale of this article is how we use development stores. This is where it gets a little tricky, but make sure to stay with us.&lt;/p&gt;

&lt;p&gt;Development stores are a way that Shopify allows its app creators to test their apps on a storefront. It is a barebones store that you will have created if you ran that &lt;code&gt;npm run dev&lt;/code&gt; command we discussed in the first part of the article, or you can choose to develop your app on your own store should you configure your app to do so (this won't be covered in this article).&lt;/p&gt;

&lt;p&gt;So you and your friend want to develop your app but you can't connect to the same store? Well that's easy... make two more stores. I know this may seem like a cop out, but trust us, we tried a ton of different ways and this is the best way to go about it.&lt;/p&gt;

&lt;p&gt;Remember those branches we created earlier in Git? Well here's how this goes down. We want to create a development store for you and your teammate (2 so far), then we want to create a development store for your &lt;code&gt;development&lt;/code&gt; branch in your remote repository (3 in total). Then we're going to create 2 more apps in the Shopify dashboard, one for you and one for your teammate. In our case we have &lt;code&gt;APP NAME - USER FIRST NAME&lt;/code&gt; as our naming format to ensure we won't end up with duplicate development stores.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8jpfoxqrh0wez3dl86r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8jpfoxqrh0wez3dl86r.jpg" alt="Diagram showing how users connect to development stores with Ngrok and deploy to the cloud with git branches and a git repository"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anytime you run your code locally, we want to run it off of your Ngrok domain pointing to your personal app. The same goes for your teammate. You make your changes but you &lt;strong&gt;don't&lt;/strong&gt; track your &lt;code&gt;shopify.app.toml&lt;/code&gt; file, then your changes get merged into the dev branch which is running on its own &lt;code&gt;APP NAME - STAGING&lt;/code&gt; app installed on its own development store. Then finally merged into the &lt;code&gt;main&lt;/code&gt; branch that points to your main app and is exposed to all of your users. With this method, you can test changes on your own development stores and merge and conditionally approve change requests before they go out to your users without having to revert changes as often, as well as maintaining your own workspace and not interfering with one another.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Quick Tip - Make sure you have a separate TOML file for both your development environment and your production environment. Personally, I always get confused and irritated having to run the command to change TOML files or check which TOML file I'm using, so I just have two separate directories for my production and development environments with a TOML file in each one respectively. However, you can just be more diligent than myself and use the &lt;code&gt;npm run shopify app config use &amp;lt;CONFIG&amp;gt;&lt;/code&gt;  to specify a different configuration TOML file to point to either your personal Ngrok domain or your production app&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By now you should have a Git repository in your app with a few branches created, a Ngrok domain acting as a tunnel for your application and a few development stores connected to different versions of your app. That's great! You and your teammate can now make separate commits (after having locally tested code on your own development stores) that can be periodically approved and merged up into main where everyone can see your new feature releases. A few final notes and words of wisdom:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you choose to add a Theme App Extension UI (start playing with app blocks) you will need to have a local version of your code that is specifically acting as a local copy of the &lt;code&gt;main&lt;/code&gt; branch. If you try to run &lt;code&gt;npm run deploy&lt;/code&gt; in your development branch, it will only deploy to your development app and not your users. Read the 'Quick Tip' at the end of the last section of the article for a quick insight into using &lt;code&gt;npm run shopify app config use&lt;/code&gt; to address this in one folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not commit your TOML files&lt;/strong&gt; except for an initial commit to add your production TOML file to the repository. You don't want to overwrite your production TOML file with your local development one, or all of your app users will be routed to your laptop's development server.&lt;/li&gt;
&lt;li&gt;With Ngrok's free plan, you have a limit of 1GB of bandwidth, so try not to leave your local server running with Ngrok for too long or you may use up all of your bandwidth very quickly and be forced to stop testing features until the rolling month contract rolls over to the next month.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven't by now, please go and take a look at some of the work we're doing at &lt;a href="https://getflare.co.uk" rel="noopener noreferrer"&gt;Flare&lt;/a&gt; to try and make e-commerce more sustainable.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;/p&gt;

&lt;p&gt;~ Josh&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>tutorial</category>
      <category>ngrok</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
