<?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: gabeb03</title>
    <description>The latest articles on Forem by gabeb03 (@gabeb03).</description>
    <link>https://forem.com/gabeb03</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%2F1501534%2F5320fbf3-9350-400e-9524-aa254bbe691f.jpeg</url>
      <title>Forem: gabeb03</title>
      <link>https://forem.com/gabeb03</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gabeb03"/>
    <language>en</language>
    <item>
      <title>Stop throwing your life in the garbage. Automate.</title>
      <dc:creator>gabeb03</dc:creator>
      <pubDate>Fri, 17 Oct 2025 19:53:08 +0000</pubDate>
      <link>https://forem.com/gabeb03/stop-throwing-your-life-in-the-garbage-automate-1db</link>
      <guid>https://forem.com/gabeb03/stop-throwing-your-life-in-the-garbage-automate-1db</guid>
      <description>&lt;p&gt;Every business needs little automations. &lt;/p&gt;

&lt;p&gt;Stuff like writing custom reports that pull from a bunch of data warehouses. Menial tasks.&lt;/p&gt;

&lt;p&gt;There is never going to be a company built around making my company’s custom reports because no other company needs that service. Paying an agency to build this isn’t worth the cost. There’s the upfront cost to the agency, then you have to pay to scale the internal tool as the company scales. That means hiring an infra team. A good one will put you back $300K/year at least. &lt;/p&gt;

&lt;p&gt;Before &lt;a href="https://gadget.dev?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=automate" rel="noopener noreferrer"&gt;Gadget&lt;/a&gt;, these little automations never got built. Companies just lived with inefficiencies forever. Months of people’s lives were thrown into the garbage doing menial tasks that could be easily automated. &lt;/p&gt;

&lt;p&gt;Gadget brought the cost of building custom software down to the cost of lunch every month. You don’t need an infra or internal tools team. Gadget apps scale to millions of users out of the box. &lt;/p&gt;

</description>
      <category>gadget</category>
      <category>programming</category>
    </item>
    <item>
      <title>Build an AI Concierge App in ChatGPT</title>
      <dc:creator>gabeb03</dc:creator>
      <pubDate>Fri, 17 Oct 2025 19:51:31 +0000</pubDate>
      <link>https://forem.com/gabeb03/build-an-ai-concierge-app-in-chatgpt-4gei</link>
      <guid>https://forem.com/gabeb03/build-an-ai-concierge-app-in-chatgpt-4gei</guid>
      <description>&lt;p&gt;&lt;a href="https://openai.com/index/introducing-apps-in-chatgpt/" rel="noopener noreferrer"&gt;OpenAI announced apps in ChatGPT&lt;/a&gt;, and it's under-hyped. Billion dollar businesses could be built on this platform.&lt;/p&gt;

&lt;p&gt;ChatGPT apps respond to natural language, so businesses will have to optimize for specific prompts just like they optimize for SEO keywords.&lt;/p&gt;

&lt;p&gt;One such prompt is "Find me the best dermatologist near me that takes Blue Cross". The ChatGPT app would answer the prompt by connecting to insurance networks and rendering an interactive map and booking UI.&lt;/p&gt;

&lt;p&gt;With some help from &lt;a href="https://app.gadget.dev/auth/team/create-app?fork-app=openai-apps-sdk-todos.gadget.app" rel="noopener noreferrer"&gt;Gadget's OpenAI SDK template&lt;/a&gt;, I was able to get this exact app up and running in a few hours. It uses dummy data, but it's good enough to show to potential customers and validate this idea.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/R_fHeBBztHY"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;If you're curious, you can &lt;a href="https://app.gadget.dev/auth/fork?domain=ai-health-concierge.gadget.app" rel="noopener noreferrer"&gt;fork my Gadget app&lt;/a&gt; and spin up your own ChatGPT app.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>webdev</category>
      <category>openai</category>
    </item>
    <item>
      <title>Build a Discord support bot from scratch</title>
      <dc:creator>gabeb03</dc:creator>
      <pubDate>Thu, 11 Sep 2025 15:45:08 +0000</pubDate>
      <link>https://forem.com/gabeb03/build-a-discord-support-bot-from-scratch-1hlm</link>
      <guid>https://forem.com/gabeb03/build-a-discord-support-bot-from-scratch-1hlm</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;Demo&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;I always found it annoying when I was following a tutorial, and they didn’t show you what the final result was upfront. Let’s not do that. Here's a video walkthrough of how the Discord support bot will work:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Tk29XrQtzBA"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here’s the TL;DR:&lt;/p&gt;

&lt;p&gt;I work for a company called &lt;a href="https://gadget.dev?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=discord" rel="noopener noreferrer"&gt;Gadget&lt;/a&gt;. We need to start measuring the quality of support we’re giving our users. What gets measured, gets managed.&lt;/p&gt;

&lt;p&gt;When a user posts a question in our #help &lt;a href="https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ" rel="noopener noreferrer"&gt;Discord forum channel&lt;/a&gt;, and the question gets answered by a Gadget expert, the Discord bot will ask the user to rate the quality of the answer. &lt;/p&gt;

&lt;p&gt;Gadget is a platform that handles all the infrastructure (database, backend &amp;amp; frontend deployment) for you. I’m biased, but it’s genuinely the best way to make any kind of web app. Naturally, this Discord bot was made using the Gadget platform.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Create a Discord developer account&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Go to &lt;a href="http://discord.com/developers" rel="noopener noreferrer"&gt;discord.com/developers&lt;/a&gt; and make your Discord account into a developer account.&lt;/p&gt;

&lt;p&gt;Once you've created a developer account, create a new application. I called my app "Gizmo".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fpqvk84meg4zgb7hdlgbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fpqvk84meg4zgb7hdlgbf.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Setup Gadget&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Instead of making you faff about hosting the Discord bot and writing the code, I made a &lt;a href="https://gadget.dev?utm_source=devto&amp;amp;utm_medium=community&amp;amp;utm_campaign=discord" rel="noopener noreferrer"&gt;Gadget&lt;/a&gt; project that you can just fork. &lt;/p&gt;

&lt;p&gt;That way, your Discord bot is securely hosted and the code to get the bot to interact with users is already written. The code is pretty simple: it sets up a command called &lt;code&gt;/close&lt;/code&gt;. The idea is when someone asks a question by making a new thread in the Gadget server’s #help &lt;a href="https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ" rel="noopener noreferrer"&gt;forum channel&lt;/a&gt;, and a Gadget expert answers the question, they can close the thread by running the &lt;code&gt;/close&lt;/code&gt; command. When this command is run, a message pops up and asking the reader to rate the quality of the answer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F4z0swhjr5utci8kq6y5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F4z0swhjr5utci8kq6y5v.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyone can vote on the quality of the answer, not just the original poster. This makes sense for us because devs often search through old support tickets to see if someone has run into their issue before. Those devs should be able to give feedback even if they haven’t contributed to the thread.&lt;/p&gt;

&lt;p&gt;Fork the project &lt;a href="https://app.gadget.dev/auth/fork?domain=gizmo-support-bot.gadget.app" rel="noopener noreferrer"&gt;here&lt;/a&gt;, then give your application a name. Now you have to connect the Gadget app with the bot you set up on the Discord developer portal. To do that, you need to find the bot’s token. &lt;/p&gt;

&lt;p&gt;Go back to the developer portal, then press the “Reset Token” button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fe0mr263k2lo9xx5qh845.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fe0mr263k2lo9xx5qh845.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy your new token. Go to Gadget, then Settings &amp;gt; Environment Variables and set &lt;code&gt;DISCORD_BOT_TOKEN&lt;/code&gt; to the token you just copied.&lt;/p&gt;

&lt;p&gt;The last thing you need to do is tell the Gadget app which servers and channels your bot is allowed in. You need the guild ID (server == guild in Discord) and the channels ID. Now that you are registered as a developer, you can right click on the server icon and channel icon to find their respective IDs.&lt;/p&gt;

&lt;p&gt;Paste the server ID into &lt;code&gt;DISCORD_GUILD_ID&lt;/code&gt; and the channel ID(s) into &lt;code&gt;DISCORD_SUPPORT_CHANNEL_IDS&lt;/code&gt;. If you have more than one channel ID, separate them by commas like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
123,234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  &lt;strong&gt;Install the Discord bot&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;Select "OAuth2" from the left menu. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Foj82i4jl9ckcyho00uio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Foj82i4jl9ckcyho00uio.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then in the “OAuth2 URL Generator”, select “bot”, then in “Bot Permissions”, select “Administrator”. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fg58tbbb2yvneym11pg08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fg58tbbb2yvneym11pg08.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will give your bot permission to do everything in the server it’s installed in. This is good for testing. If your bot will be used by other servers, narrowing down the permissions to only the essentials might be a good idea. My bot is an internal tool, so it’s not a huge deal.&lt;/p&gt;

&lt;p&gt;Copy the generated URL and paste it in a new tab. Select the server where you want to install your bot. &lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Test the bot&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;The &lt;code&gt;/close&lt;/code&gt; command makes the bot ask the user for feedback. When you run that command, the bot should respond with the following message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fc0x159w8l23iryl2zqcz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fc0x159w8l23iryl2zqcz.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anyone on the server can rate the quality of the answer, not just the original poster. This &lt;code&gt;/close&lt;/code&gt; command only makes sense in &lt;a href="https://support.discord.com/hc/en-us/articles/6208479917079-Forum-Channels-FAQ#h_01JAZAWCX3QQ6QXV4G1ZG3E968" rel="noopener noreferrer"&gt;forum posts&lt;/a&gt;. The &lt;code&gt;/close&lt;/code&gt; command won’t execute in normal text channels.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;Check the dashboard&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;The Gadget template doesn’t just host the discord bot, it has a frontend that visualizes the feedback data. Go to the Gadget template and press the play icon to navigate to the Gadget frontend: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Ft7p6ynsc6q4xhhvfzsei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Ft7p6ynsc6q4xhhvfzsei.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log into the app, then you should see a table with all the customer feedback:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F3td6mc925ixtfobgyfuy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F3td6mc925ixtfobgyfuy.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shazam! You now have a completely customizable Discord support system that is lightweight and free forever.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;More ideas&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;This is just the tip of the iceberg. There are a bunch of features you could add to this project to help your support team go even faster. Here are a few I can think of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Auth  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://railway.com/discord-link" rel="noopener noreferrer"&gt;Railway Discord server&lt;/a&gt; has this awesome thing where users get access to the community by authenticating via their Railway accounts. This way, everyone on the server is serious enough to create a new account, which filters out most bots and spammers. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Custom ticket management  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tools like Linear or Zendesk are awesome, but there’s always that one feature that is missing. You can turn this project into a full-blown ticket management software that tracks which questions are still unanswered, and increases the priority of the ticket the longer it stays unanswered.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Monthly reports  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Executives LOVE reports, but they are a pain to make. Making reports is also a distraction from your main job (if making reports &lt;em&gt;is&lt;/em&gt; your main job, have mercy on your soul).
&lt;/li&gt;
&lt;li&gt;You can automate reports by setting up a &lt;a href="https://docs.gadget.dev/guides/actions/background#customizing-background-actions" rel="noopener noreferrer"&gt;Gadget background action&lt;/a&gt; that reads the past month’s stats from the Gadget database and sends an &lt;a href="https://docs.gadget.dev/guides/plugins/authentication/email-pass#custom-email-templates" rel="noopener noreferrer"&gt;automated email&lt;/a&gt; to your boss.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>discord</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to connect Shopify to Notion</title>
      <dc:creator>gabeb03</dc:creator>
      <pubDate>Wed, 20 Aug 2025 19:57:54 +0000</pubDate>
      <link>https://forem.com/gabeb03/how-to-connect-shopify-to-notion-2ac7</link>
      <guid>https://forem.com/gabeb03/how-to-connect-shopify-to-notion-2ac7</guid>
      <description>&lt;p&gt;Shopify gives merchants a rich dataset. The platform makes it easy to keep track of key metrics like monthly revenue, gross margins, etc. The problem is if your company uses Notion, all the data relating to the company is on Notion &lt;em&gt;except sales data&lt;/em&gt;. That's stuck in Shopify.&lt;/p&gt;

&lt;p&gt;This tutorial will show you how to free sales data from Shopify and send it to Notion automatically. Whenever a new order is created in Shopify, the Notion page is updated.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll learn how to automate tasks in Shopify using &lt;a href="https://gadget.dev/?utm_source=youtube&amp;amp;utm_medium=social&amp;amp;utm_campaign=notion" rel="noopener noreferrer"&gt;Gadget&lt;/a&gt;. This is a super valuable skill. Companies pay big bucks (thousands and thousands of $$) for automatons like this one.&lt;/p&gt;

&lt;p&gt;The integration works like this: when a new order is created in Shopify, a backend service (built with &lt;a href="https://gadget.dev/?utm_source=youtube&amp;amp;utm_medium=social&amp;amp;utm_campaign=notion" rel="noopener noreferrer"&gt;Gadget&lt;/a&gt;) calculates the relevant financial metrics and pushes them as new entries into a Notion database.&lt;/p&gt;

&lt;p&gt;By the end, you'll have a fault-tolerant system that handles rate limiting and ensures data accuracy. Instead of setting up all the rate limiting stuff ourselves, we can let Gadget handle it, and just write the business logic.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Access to the Notion &lt;a href="https://steampunk-education.notion.site/Finance-Dashboard-249719c13e368029a77be68a7b824e35" rel="noopener noreferrer"&gt;finance dashboard template&lt;/a&gt;. This is where the sales data will be rendered. You can create your own Notion page to display the sales data, but I recommend you follow along with this page, then edit the page once everything is working. Just my 2 cents.&lt;/li&gt;
&lt;li&gt;A Shopify store with products set up (including the cost for each product in your store).

&lt;ul&gt;
&lt;li&gt;You can set the cost of a product by going to Products &amp;gt; Select Product &amp;gt; Inventory &amp;gt; Cost per item. By default, this is set to $0, so update them manually.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;A Notion workspace.&lt;/li&gt;

&lt;li&gt;A free &lt;a href="https://gadget.dev/?utm_source=youtube&amp;amp;utm_medium=social&amp;amp;utm_campaign=notion" rel="noopener noreferrer"&gt;Gadget&lt;/a&gt; account&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up the Notion Finance Dashboard
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://steampunk-education.notion.site/Finance-Dashboard-249719c13e368029a77be68a7b824e35" rel="noopener noreferrer"&gt;finance dashboard template&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Duplicate&lt;/strong&gt; in the top-right corner to copy it to your Notion workspace.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fzsypct09muihha6n2zt4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fzsypct09muihha6n2zt4.png" alt=" " width="550" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Select your target workspace (personal or team) and confirm.&lt;/li&gt;
&lt;li&gt;Scroll to the bottom—the charts are powered by a single database. We'll write new rows to this database from Gadget. You can delete the dummy data I have in this database.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Create a Gadget App and Connect to Shopify
&lt;/h2&gt;

&lt;p&gt;Gadget acts as the "bridge" over the "river" between Shopify and Notion. It listens for new orders via webhooks and forwards calculated data to Notion.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F5fsphc8ic5a7co48ityd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F5fsphc8ic5a7co48ityd.png" alt=" " width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to gadget.dev and create a new account if needed.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create New App&lt;/strong&gt; &amp;gt; Select &lt;strong&gt;Shopify app&lt;/strong&gt; &amp;gt; Leave as &lt;strong&gt;Custom app&lt;/strong&gt; &amp;gt; Continue.&lt;/li&gt;
&lt;li&gt;Keep defaults for frontend framework (Remix) and language (TypeScript).&lt;/li&gt;
&lt;li&gt;Name your app (e.g., &lt;code&gt;finance-dashboard&lt;/code&gt;) and confirm.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings&lt;/strong&gt; (bottom-left) &amp;gt; &lt;strong&gt;Plugins&lt;/strong&gt; &amp;gt; &lt;strong&gt;Shopify&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Go through the steps to connect your Gadget app to Shopify.&lt;/li&gt;
&lt;li&gt;Select these Shopify data models:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Orders&lt;/strong&gt;: For new order triggers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order Line Items&lt;/strong&gt;: For prices and quantities.
&lt;img src="https://media2.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%2Ffd2wwbnvwa2m9mmq7hr6.png" alt=" " width="800" height="955"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Products&lt;/strong&gt;: Hidden in search—needed for variants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product Variants&lt;/strong&gt;: For linking to inventory.
&lt;img src="https://media2.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%2Frjugnstw7dim0auwcj1p.png" alt=" " width="800" height="846"&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory Items&lt;/strong&gt;: For costs.
&lt;img src="https://media2.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%2Fvjioxofs4aiuubfnd5oz.png" alt=" " width="800" height="284"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the search box to find these. Only enable &lt;strong&gt;Read&lt;/strong&gt; permissions (no writes needed).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Confirm the setup. Gadget auto-creates webhooks and data models.&lt;/li&gt;
&lt;li&gt;Fill out Shopify's protected customer data access form: Select &lt;strong&gt;App Functionality&lt;/strong&gt; as the reason (to read prices/costs for Notion syncing) and save.&lt;/li&gt;
&lt;li&gt;Once the form is filled out, go back to Gadget. Install the app on your Shopify store when prompted.&lt;/li&gt;
&lt;li&gt;In Gadget, go to &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Plugins&lt;/strong&gt; &amp;gt; &lt;strong&gt;Shopify&lt;/strong&gt;—it should show as active.&lt;/li&gt;
&lt;li&gt;On the left side bar go to &lt;strong&gt;Installs&lt;/strong&gt; &amp;gt; Sync data. This pulls your Shopify data into Gadget's database for redundancy and rate-limit protection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F2wnxcrn0c2k7el672996.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F2wnxcrn0c2k7el672996.png" alt=" " width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify in &lt;code&gt;api/models/shopifyOrder/data&lt;/code&gt;: Check &lt;strong&gt;Shopify Orders&lt;/strong&gt; for your orders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Gadget's syncing ensures data consistency—if Shopify rate-limits requests during a sales surge, Gadget retries later, keeping Notion in sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Configure the Notion Integration
&lt;/h2&gt;

&lt;p&gt;We will create a custom &lt;a href="https://developers.notion.com/docs/getting-started#internal-integrations" rel="noopener noreferrer"&gt;internal integration&lt;/a&gt; in Notion to give Gadget access to the finance dashboard.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://www.notion.so/profile/integrations/" rel="noopener noreferrer"&gt;notion.so/profile/integrations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New Integration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name it (e.g., "Finance Dashboard Integration").&lt;/li&gt;
&lt;li&gt;Select your workspace (where the finance dashboard lives).&lt;/li&gt;
&lt;li&gt;Save and go to &lt;strong&gt;Access&lt;/strong&gt; &amp;gt; Grant the integration access to the finance dashboard Notion page: Search for it, select, and update.&lt;/li&gt;
&lt;li&gt;Back in the &lt;strong&gt;Configuration&lt;/strong&gt; tab, copy the &lt;strong&gt;Internal Integration Secret&lt;/strong&gt; (API key).&lt;/li&gt;
&lt;li&gt;In Gadget, go to &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Environment Variables&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add:

&lt;ul&gt;
&lt;li&gt;Key: &lt;code&gt;NOTION_API_KEY&lt;/code&gt; | Value: Paste the secret (lock it for security if needed).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Get the Notion database ID:

&lt;ul&gt;
&lt;li&gt;Scroll to the bottom of the finance database. Open the finance dashboard database as a full page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fkn6mlltcs79xxt1fr4af.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fkn6mlltcs79xxt1fr4af.png" alt=" " width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy the ID from the URL (between the last &lt;code&gt;/&lt;/code&gt; and &lt;code&gt;?&lt;/code&gt;). 

&lt;ol&gt;
&lt;li&gt;Add another variable: Key: &lt;code&gt;NOTION_DB_ID&lt;/code&gt; | Value: Paste the ID. &lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Install Dependencies and Write the Code
&lt;/h2&gt;

&lt;p&gt;Gadget auto-generates boilerplate code. We'll extend it to calculate metrics and update Notion.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Gadget's top search bar, run: &lt;code&gt;yarn add @notionhq/client&lt;/code&gt; to install the Notion client library.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Files&lt;/strong&gt; &amp;gt; &lt;strong&gt;api&lt;/strong&gt; &amp;gt; &lt;strong&gt;models&lt;/strong&gt; &amp;gt; &lt;strong&gt;shopifyOrder&lt;/strong&gt; &amp;gt; &lt;strong&gt;actions&lt;/strong&gt; &amp;gt; &lt;strong&gt;create.ts&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Optional: Use a local editor instead of the browser-based one:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Go to the cloud icon and follow the instructions:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fu512p326vt3bepoq5tve.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fu512p326vt3bepoq5tve.png" alt=" " width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Frnd19akjq6zorvgvl93a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Frnd19akjq6zorvgvl93a.png" alt=" " width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now you can use &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;your favorite editor&lt;/a&gt; to edit your Gadget app's code. Changes sync back to Gadget in real-time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Replace the code in &lt;code&gt;api/models/shopifyOrder/actions/create.ts&lt;/code&gt; with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;applyParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ActionOptions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gadget-server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;preventCrossShopDataAccess&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gadget-server/shopify&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@notionhq/client&lt;/span&gt;&lt;span class="dl"&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;notion&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;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTION_API_KEY&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;updateNotionDb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;notion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;database_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;database_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTION_DB_ID&lt;/span&gt; &lt;span class="o"&gt;??&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;properties&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;Order ID&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&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;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&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;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&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;span class="na"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&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;Created At&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;************ Notion response ************&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;connections&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;applyParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;preventCrossShopDataAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSuccess&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;connections&lt;/span&gt;&lt;span class="p"&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="c1"&gt;// Write new order to Notion&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Calculating sales and COGS for order &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;lineItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shopifyOrderLineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;inventoryItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;:&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="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Found &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lineItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; line items for order &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;totalSales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;totalCOGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;itemsWithoutInventory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &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;lineItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;lineItems&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&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;itemQuantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&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;itemSales&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;itemPrice&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;itemQuantity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;totalSales&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;itemSales&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;inventoryItem&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;cost&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;itemCost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inventoryItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost&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;itemCOGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;itemCost&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;itemQuantity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="nx"&gt;totalCOGS&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;itemCOGS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Can't find inventory item for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lineItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Can't find variant&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="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="s2"&gt;`Order &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; totals - Sales: $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;totalSales&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; COGS: $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;totalCOGS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalCOGS&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateNotionDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sales&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalSales&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateNotionDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cost of Goods&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalCOGS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateNotionDb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Margin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;totalSales&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;totalCOGS&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;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;logger&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;error&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;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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&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;error&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;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActionOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;actionType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create&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;h3&gt;
  
  
  Code Explanation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Imports and Notion Client&lt;/strong&gt;: Sets up Gadget utilities and initializes the Notion client with your API key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;updateNotionDb Function&lt;/strong&gt;: Creates a new row in the Notion database. It populates columns: "Order ID" (title), "Type" (select: Sales, Cost of Goods, or Margin), "Value" (number), and "Created At" (date via ISO string).

&lt;ul&gt;
&lt;li&gt;Why three rows per order? Notion requires this structure for grouping multi-line charts by a shared ID (Order ID). In other words: Notion's charting sucks. If you're interested, checkout &lt;a href="https://youtu.be/i_D8MlBAk0A?feature=shared&amp;amp;t=2065" rel="noopener noreferrer"&gt;this excellent tutorial on Notion charting&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;run Function&lt;/strong&gt;: Gadget's entry point—applies params, prevents cross-shop access, and saves the order record.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;onSuccess Function&lt;/strong&gt;: Triggers after saving the order to the Gadget database:

&lt;ul&gt;
&lt;li&gt;Fetches line items for the new order using Gadget's API (GraphQL-like query).&lt;/li&gt;
&lt;li&gt;Loops through items: Calculates sales (price × quantity) and COGS (cost × quantity).&lt;/li&gt;
&lt;li&gt;If COGS &amp;gt; 0, calls &lt;code&gt;updateNotionDb&lt;/code&gt; three times (once per metric).&lt;/li&gt;
&lt;li&gt;Error handling: Catches and logs issues to prevent crashes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Test the Integration
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;In Shopify, go to &lt;strong&gt;Orders&lt;/strong&gt; &amp;gt; &lt;strong&gt;Create Order&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2F2pq8x4tqahtx1ex0s2pm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2F2pq8x4tqahtx1ex0s2pm.png" alt=" " width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add products, mark as paid, and create.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.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%2Fbg2rte299u5skk8to2dc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.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%2Fbg2rte299u5skk8to2dc.png" alt=" " width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check Gadget's &lt;strong&gt;Logs&lt;/strong&gt; (filter by error/debug) for issues.&lt;/li&gt;
&lt;li&gt;Refresh your Notion dashboard—new bars should appear for the order's sales, COGS, and margin.&lt;/li&gt;
&lt;li&gt;Verify in Notion's database: Three new rows per order, grouped by Order ID.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If nothing updates, check logs: Validation errors often stem from mismatched column names (case-sensitive) or missing costs in Shopify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No Data in Notion&lt;/strong&gt;: Confirm environment variables, database ID, and integration access. Resync in Gadget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting&lt;/strong&gt;: Gadget handles this—data will sync eventually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Errors in Logs&lt;/strong&gt;: Debug with the built-in logger (e.g., missing variants mean no COGS calculated).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph Issues&lt;/strong&gt;: Ensure Notion column names match exactly (e.g., "Value" with capital V).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Questions?&lt;/strong&gt; Join the &lt;a href="https://ggt.link/discord" rel="noopener noreferrer"&gt;Gadget Developer Discord&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Good on you for sticking to this tutorial to the end! Not a lot of people have the focus that you have.&lt;/p&gt;

&lt;p&gt;If you like building custom software, and want to make bank doing it for big companies, &lt;a href="https://www.linkedin.com/in/gabe-braden/" rel="noopener noreferrer"&gt;checkout my other projects&lt;/a&gt;. Feel free to copy these projects and make money off them. Life isn't a zero sum game ❤️&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
