Close

Sign up to Gadget

Sign Up

Build an Automated Product Tagging App for a Shopify Store

Mohammad Hashemi
November 22, 2021

Use-case: A Shopify merchant needs an automated way to ‘tag’ products being added to their Shopify store inventory.  They source hundreds of products weekly from various dropshippers, and upload the unstructured data to Shopify programmatically. Because the data is unstructured, Shopify is unable to power the merchant’s storefront search. While the merchant has the ability to add tags inside the Shopify Admin, the experience of doing this on hundreds of products on a weekly basis is time-consuming.

Instead, they opted to build a custom Shopify app on Gadget that would run every new product description through an automated tagging script.


Follow along and build this app in under 15 minutes

In this example, we’ll show you how to build a custom product tagging system that listens to the <inline-code>product/create<inline-code> and <inline-code>product/update<inline-code> webhooks from Shopify, runs product descriptions through a tagging script and sets tags back in Shopify.

Connecting Shopify 

Our first step will be to set up a Gadget project, and connect our backend to a Shopify store via Gadget Connections.

To do this: 

  • Go to Connections, then select Add Connection
  • Enable the read and write scopes for the Products row, and select the underlying models you wish to import into Gadget. We decide to copy the Product model and all of its associations.
  • Enter your Shopify custom app credentials, which can be generated inside your Shopify Partner dashboard.
  • Click Submit

At this point, Gadget copies the selected Shopify models, their types, validations and associations into your Gadget backend. These models are ready to process webhooks as soon as you install the app on a Shopify store.

We opted to install this app on our development store, using the Test on Development Store link on our Shopify Partner dashboard. Gadget handled the OAuth dance, and once we had granted the requested API scopes, the connection was established.

Gadget keeps your app in sync by generating a CRUD API around each of your cloned models, and wiring up each of the API actions to their corresponding Shopify webhook. So, if the Shopify store fires a <inline-code>product/create<inline-code> webhook, Gadget will run your Create action on the Product model. By default, this action uses the incoming params from the webhook to creates a record in your database. Similarly, if Shopify fires the <inline-code>product/update<inline-code> webhook, Gadget will run your Update action which updates the record with the incoming params.

What’s special about these actions is that they are yours. You can change what happens when the action runs by adding Effects defined in code.

Build your tagging script

The next step is to build out our tagging script. Let's start with a list of vetted keywords that we can use to power the script. These keywords can be different types of products or brands:

  • Add a model and call it Allowed Tag (api identifier: <inline-code>allowedTag<inline-code>) and add a single field called Keyword to it.
  • Gadget instantly creates and documents a CRUD API for this model. Using the GraphQL playground, we can make a bulk Create call to our Allowed Tag model and populate it with many keywords:

mutation CreateAllowedTag {
  createAllowedTag(allowedTag:{keyword: "sweater"}) {
    success
    errors {
      message
    }
    allowedTag {
      id
      keyword
      createdAt
    }
  }
}

Once we have keywords to check against, we can write our tagging script. Because we want this script to run every time a product record is created or updated, we'll add an Effect to the Create and Update actions on the Shopify Product state machine:

  • Go to the behavior page for Shopify Product
  • Add a Run Effect to the Create action, naming it <inline-code>applyTags.js<inline-code>
  • Use the following snippet to run every create webhook through your tagging script, which cross-references the <inline-code>body<inline-code> of the incoming payload against the keyword list by making an internal API request to Gadget . Should any words match, they're sent back to Shopify as new tags for the product.

module.exports = async ({ api, record, params, logger, connections}) => {
 if (record.changed("body")) {
   // get a unique list of words used in the record's description
   let newTags = [...new Set(record.body.split(" "))]
 
   // filter down to only those words which are allowed
   const allowedTags = (await api.allowedTag.findMany()).map(tag => tag.keyword)
   const finalTags = newTags.filter(tag => allowedTags.includes(tag))
   logger.info({newTags, allowedTags, finalTags}, "applying new tags")
 
   const shopify = connections.shopify.forShop('add-your-store-name.myshopify.com')
   await shopify.product.update(params.id, {tags: finalTags.join(',')})
 }
};

As you can see, there's little code to write. Gadget gives us a <inline-code>connections<inline-code> object as an argument to our effect function, which has an already authenticated API client for Shopify ready to go. We use this object to make API calls back to Shopify to update the tags and complete the process.

Now, we only did this on Create. To have the same behavior when a product is updated, we add the same <inline-code>applyTags.js<inline-code> file as another Run Effect on the Update action.

We're done, let's test it out!

That's all there is to this app, so let's test it out! Go back to the Connections page and click Sync on the connected store. Gadget will fetch each of the records in Shopify and run them through your actions. Not only will this populate your Gadget backend with the store's inventory, but it will also run the effects we added, updating the tags for each synced product.

The net result is we have tags being updated in Shopify each time there is a match against our Allowed Tags list. And we were able to get all of this done in 10 minutes and under 10 lines of code.

For more examples on what you can build using the Shopify Connection, check out these additional blog posts:

Under the hood