Sign up to Gadget

Start for free

Build an Automated Product Tagging app for a Shopify store



Let's build an automated product tagger with an admin-embedded app in 20 minutes.




Build an Automated Product Tagging app for a Shopify store

Riley Draward
April 12, 2023

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 can add tags inside the Shopify Admin, the experience of doing this on hundreds of products weekly is time-consuming.

To solve this, the merchant wants to build a custom Shopify app on Gadget that will run every new product description through an automated tagging script.

In this example, we'll show you how to build a custom product tagging app that listens to the product/create and product/update webhooks from Shopify, runs product descriptions through a tagging script, and sets tags back in Shopify.


To get the most out of this tutorial, you will need:
    - a Shopify Partners account
    - a development store
    - at least one product in your store that has a product description

You can fork this Gadget project and try it out yourself.

You will still need to set up the Shopify Connection after forking. Continue reading if you want to learn how to connect Gadget to a Shopify store!

Fork on Gadget

Prefer to watch a video? You can watch how to build this step-by-step by in the video below.

Create a Gadget app and connect to Shopify

Our first step will be to set up a Gadget project and connect our backend to a Shopify store via the Shopify connection. Create a new Gadget application at

Because we are adding an embedded frontend, we are going to build an app using the Partners connection.

Connect to Shopify through the Partners dashboard

Both the Shopify store Admin and the Shopify Partner Dashboard have an Apps section. Ensure that you are on the Shopify Partner Dashboard before continuing.

  • Click the Create App button
  • Click the Create app manually button and enter a name for your Shopify app
  • Go to the Connections page in your Gadget app and click on Shopify
  • Copy the Client ID and Client secret from your newly created Shopify app and paste the values into the Gadget Connections page
  • Click Connect on the Gadget Connections page to move to scope and model selection

Now we get to select what Shopify scopes we give our application access to, while also picking what Shopify data models we want to import into our Gadget app.

  • Enable the read and write scopes for the Shopify Products API, and select the underlying Product model that we want to import into Gadget
  • Click Confirm

We have successfully created our connection!

Now we want to connect our Gadget app to our custom app in the Partners dashboard.

  • In your Shopify app in the Partners dashboard, click on App setup in the side nav bar so you can edit the App URL and Allowed redirection URL(s) fields
  • Copy the App URL and Allowed redirection URL from the Gadget Connections page and paste them into your custom Shopify App

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 Partners dashboard. Gadget handles OAuth for us, all we need to do is grant permission to the API scopes and the connection is made.

Add new model for tag keywords

The next step is to create a list of vetted keywords that we can use to power our tagging script. These keywords can be different types of products or brands. Make sure to add keywords that will be found in your products' descriptions!

  • Add a model and call it Allowed Tag (api identifier: allowedTag)
  • Add a single field called Keyword to our new Allowed Tag model

Gadget instantly creates and documents a GraphQL CRUD API for this model.

Using the API Playground, we can make a Create call to our Allowed Tag model in order to store a new keyword.

We can run the same mutation again with a different keyword value to store additional keywords.

We can also check to make sure our tag keywords have been saved. Go to the Data page for the Allowed Tag model and you should be able to see an entry for any tags that have been added.

Build your tagging script

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

What makes Actions special is that they can be completely customized. You can change what happens when the action runs by adding Code Effects that you define.

Now that 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 Model page for Shopify Product and select the create action
  • Add a new Success Effect by clicking on the Add Code Snippet button. A new code file will be generated automatically
  • Click on the Go to file button to open up the file editor

Use the following snippet to run every create webhook through your tagging script, which cross-references the body 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.

That's not a lot of code!

Gadget gives us a connections 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.

We also use Gadget's changed helper on our record to avoid entering an infinite loop. This looping can occur when a Shopify webhook triggers a Code Effect that updates our Shopify store. Because we have added this change detection, we can add the same onCreate.js file as a Success Effect on the Update action.

The record.changed helper is a special field that Gadget has included to help prevent an infinite loop when updating Shopify records.

When we call shopify.product.update(...) the product in our Shopify store will be updated. This update action will fire Shopify's products/update webhook. If we are using this webhook as a trigger for running custom code that updates a product, we will be stuck in an endless loop of updating our products and running our custom code.

We can use record.changed to determine if changes have been made to the key on this record and only run our code if changes have occurred.

For more info on change tracking in Gadget, refer to the documentation.

Shopify admin frontend

New Gadget apps include a frontend folder. When you set up a Shopify connection, Gadget automatically makes changes to this frontend folder by:

  • initializing your Gadget API client in api.js
  • setting up a default React app in main.jsx
  • adding a routing example in App.jsx
  • has two examples of rendered pages and navigation with ShopPage.jsx and AboutPage.jsx

Additional packages have also been added to your package.json upon connecting to Shopify, including @gadgetinc/react which allows for the use of Gadget's handy React hooks for fetching data and calling your Gadget project's API, and @shopify/polaris which allows you to use Shopify's Polaris components out of the box when building embedded Shopify apps.

Add frontend capabilities to an existing app

If you already have a copy of the product tagger backend you can add frontend capabilities to your app instead of rebuilding the backend. To enable frontends on existing apps go to your Gadget app's Home page and click the Enable frontend button.

Add shop tenancy and permissions to Allowed Tag model

Right now the Allowed Tag model only has a single field, Keyword. If you're building a public Shopify app, you also need to associate Keywords with individual stores so that all the shops that install your app don't share Keywords.

It's also important to grant your embedded app permission to call the create and delete actions on the Allowed Tag model. Access to custom model APIs are always disabled by default for embedded app users. If you encounter a GGT_PERMISSION_DENIED error when building an embedded app, you probably need to go into the Roles and Permissions page and grant embedded app users access to your Gadget app API.

  • Add a new field named Shop to the Allowed Tag model
  • Make Shop a Belongs To relationship field and select Shopify Shop as the related model
  • Select the has many option when defining the inverse of the relationship so that Shopify Shop has many Allowed Tag

With this added Shop relationship, you will be able to track what Keywords are used for individual shops. To automatically filter by the current shop when reading from your Allowed Tag model, you can add a Gelly snippet to enforce shop tenancy automatically.

  • Go to Settings in the nav bar and select the Roles & Permissions page
  • Enable the read, create, and delete actions for your Allowed Tag model on the Shopify App Users role
  • Click + Filter next to the read action, type allowedTag into the input, and hit Enter on your keyboard to create a new Gelly file
  • Go to the file by clicking on the File icon
  • Add the following Gelly fragment

This snippet selects all Allowed Tag records when the related shopId is equal to the shop id of the current Session. The current Session is managed for you when you connect to Shopify, and session records including the current session can be viewed on the Session Data page in Gadget.

Building the frontend

You need to create a new file, then write some React to build an embedded frontend for your tagger application.

  • Create a new file in your frontend folder called KeywordsPage.jsx. This is the file we will use to build our React frontend
  • Define a new React component in KeywordsPage.jsx. This code uses Polaris components to return a simple page with some text

  • Import the KeywordsPage component in frontend/App.jsx

  • Add KeywordsPage as the default page route, replacing the ShopPage component

Your app should now display a mostly-empty page with the text "This is an embedded Shopify app" displayed.

Gadget frontend development includes hot module reloading, so you should be able to see changes in your frontend as you write code:

  • Change the content of the Text component in frontend/KeywordsPage.jsx to read "This is an embedded product tagger" and you'll see your frontend application update automatically!

You can also use React components like you would in any other React app:

  • Create a reusable PageLayout component to handle Page setup

The PageLayout component will come in handy when we deal with page loads later in the tutorial.

Full code snippet

You can paste the following code into frontend/KeywordsPage.jsx and your frontend will be good to go! You can now add new Keywords using the embedded UI in the Shopify admin. A step-by-step walkthrough of the code follows this snippet if you are interested in learning more about using Polaris in Gadget, and using the Gadget API client and React hooks to fetch data and call CRUD actions.

Step-by-step build

Using the Gadget API to fetch data

Your Gadget API client is already set up for you in frontend/api.js! You can use this API client to fetch data from our models using the product tagger application's auto-generated API. You can also make use of some Gadget-provided React hooks that help to read (and write) using your app's API.

Fetching allowed tags

  • Import the Gadget API and React hooks into frontend/KeywordsPage.jsx

  • Use the React hooks from @gadgetinc/react to get a list of keywords

The useFindMany hook will run api.allowedTag.findMany() and return a response that has data, error, and fetching as properties. In this case, the response is named allowedTagResponse. You can use fetching to display a Polaris Spinner when the Allowed Tag data is loading, and the error property to display and handle any request errors.

  • Import the Spinner component from @shopify/polaris
  • Use PageLayout and allowedTagResponse.fetching to render a spinner when fetching data, or else a list of keywords, or a message that no keywords have been added

Your app displays a Polaris Spinner component when your request is waiting for a response and a list of existing keywords if they exist. If there are no keywords in your Gadget app's database a message will be displayed letting you know no keywords have been added.

It's not pretty (yet) but you're successfully fetching data from your backend and displaying it in an embedded frontend!

Fetching current store id

We also need the id of the current shop when adding new keywords so that created Allowed Tag records have a relationship to the current shop.

Shop tenancy is already handled on the Shop model for you so a request for a single Shopify Shop is all that is needed and the current shop will be returned. A read request grabs all non-relationship fields on a model by default. To minimize the amount of data that is being requested, a select query can also be added to the hook so that only the id field on the Shopify Shop model.

  • Add the useFindFirst React hook to fetch the current shop

  • Add the following snippet underneath the useFindMany hook that fetches the Allowed Tag model

  • Use the fetching, data, and error fields of the shopResponse to print the response to your app's UI

The shop id will soon be used to create new tags!

Using Polaris to display data

It's time to add some additional Polaris components that allow you to add and remove keywords! Here are all the @shopify/polaris components that the app will use.

  • Import the following components from Polaris

The components to focus on are the Form, TextField, and Button. You will use a Form with a TextField input box used for entering new keywords and a Button to submit the keyword. But first, you need to add some state to keep track of keywords entered into the TextField:

  • Import useState from "react"

  • Add some React state to the page to keep track of the current text entered in the TextField

Now you can use keyword to track the currently entered input value, and setKeyword to update the state when a user types or pastes into the input box. They can both be used with the Polaris TextField to enter new keywords.

  • Replace the contents of the return statement in KeywordPage.jsx with the following:

Things are coming together!

Note that you cannot submit the form yet! Next, you will write actions and use hooks to create new keywords in your Gadget app.

Using React hooks to write data

Now you need to send a request to your Gadget app's backend API to create entered keywords in your app's database. The @gadgetinc/react package also has a useAction hook that will assist with making requests to any of your model actions.

  • Add useAction to your import statement from @gadgetinc/react

The useAction hook can be used to make requests against your Gadget API. You use your Gadget project's API client to select what action will be called.

  • Add useAction hooks for the api.allowedTag.create and api.allowedTag.delete actions

The Response portion of useAction hook can be used to get the API response. The second parameter will run the action.

First, you can use createTag to send your tag state to Gadget so that entered keywords can be saved.

  • Import useCallback from react

  • Add a handleSubmit useCallback hook function that calls createTag with the keyword as an argument, and links the new Allowed Tag to the current Shopify Shop

  • Add handleSubmit to your Form component's onSubmit prop

  • Use the createResponseTag to display errors and disable the TextField and Button components when then request is fetching

Adding new keywords will work, you can test it out by entering a keyword into the input and clicking the Add keyword button.

Delete keywords

Now you can finish the embedded app by adding support for keyword deletion.

  • Create a useCallback function called removeTag to handle the deletion of keywords that accepts a tag id and sends the deleteTag request

  • Call removeTag with the Tag component's onRemove prop, passing in the tag id (this snippet also makes use of the Polaris Tag component to allow for easy removal of keywords, as well as LegacyCard and LegacyStack to make things prettier!)

Now clicking on the Tag component's "X" button should remove tags from your list!

Congrats! You have built a full-stack and fully-functional embedded product tagger application! Now you can test it out.

Test it out!

Go back to the Connections page in Gadget and click Shop Installs and then Sync on the connected store if you set up a custom app through the Partners dashboard, or just click Sync if you used the store Admin to set up your app.

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. Our tagging application will also run on products when they are added to the store, so any new products will also be tagged for us automatically.

Congratulations! In about 20 minutes you were able to build a custom app that updates tags in Shopify each time there is a match against the list of Allowed Tags.

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

Keep reading to learn about how it's built

Under the hood

We're on Discord, drop in and say hi!
Join Discord
Bouncing Arrow