How to: import the Gadget API into a Shopify storefront (product quiz)
When building storefront apps using Liquid, you may need to make requests to a Gadget backend. This guide will show you how to use your Gadget project's direct script tag in the Shopify storefront to embed a product quiz in a store.
Product recommendation quizzes help build engaging sales experiences for online shoppers by allowing them to map their problems or concerns to a product that best meets their needs. For Shopify merchants, this can be an appealing proposition! Merchants can build custom quizzes that present shoppers with a tailored experience, resulting in higher sales conversions and greater satisfaction by matching shoppers with the right products.

Before starting this guide you need the following:
- A Shopify Partners account
- A development store
- A recently-installed Shopify-developed theme (for example, the default Dawn theme)
- Advanced Some familiarity with Models and Actions in Gadget. If you are new to Gadget, start with the automated product tagger tutorial
Fork product quiz app
Good news, everyone! Instead of building an app from scratch, you are going to fork an existing product quiz app. This app already contains:
- a Shopify connection with the required scopes and data models
- data models for managing quiz data, including questions, answers, and recommended products
- backend logic for cleaning up data when a quiz is deleted
- an embedded frontend for creating and managing quizzes
Click here to fork the app:
Here is a diagram showing an overview of the app's tech stack:
The next step will be installing your quiz app on a Shopify store, but first, let's take a look at the data models and permissions that have been added for you.
Quiz data models
You need a way to create, serve, and record quiz responses in your app. The recording of responses enables you to track the conversion state mentioned in the intro, effectively making a response to a quiz a snapshot of a session that a shopper has with your quiz.
You need some models to capture the structure of the product quiz. A quiz
has many questions
, and each question
can have multiple possible answers
. Each answer
has a recommendedProduct
that relates to a product in a Shopify store. This means that each question answered will be linked to a product to offer shoppers.
You also need a model to capture user responses to the quiz, quizResult
. This will capture shopper information, such as an email address, and a list of recommended products so follow-up emails can be sent once a quiz is completed. The products recommended to shoppers are stored in a separate model, shopperSuggestion
. Keeping these recommendations in a separate model means you can update quiz questions and answers while maintaining recommendations for shoppers who took previous versions of the quiz.

Here's a diagram to demonstrate what relationships our models will have with each other:
Roles and permissions
The quiz app has two different sets of users. The merchant who is creating the quiz, and shoppers who take the quiz. Merchants will create quizzes in their store admin and will have the Shopify App User role in Gadget. Shoppers will be unauthenticated
, so all quiz-related data needs to be readable by anyone.
Gadget helps manage these different roles using the Roles & Permissions page, which you can access through the Settings option in the left nav.
Permissions have already been granted to this app. The shopify-app-users
role, which is assigned to merchants when building a quiz in the store admin, has been granted read and write access to the quiz, question, answer, and recommendedProduct models. unauthenticated
users, which are shoppers, can read data from the quiz, question, answer, recommendedProduct, and shopifyProduct models. unauthenticated
users also need to be able to save their responses. They have been granted permission to create
new records of the quizResult and shopperSuggestion models.
You can view these permissions by clicking on Settings and then Roles & Permissions in the left nav.

Connecting to Shopify
To recommend products to shoppers, we'll need product data in our app that we can map to the results of a product recommendation quiz. Using Gadget's Connections feature, we can connect our app to a Shopify store and pull product data right from the shop.
The Shopify connection provides us with access to any of the models surfaced in Shopify's Admin API, as well as an authenticated client and webhook consumption. This connection also allows us to sync data between Shopify and Gadget, both scheduled and on-demand.
Because the quiz app was forked, the Shopify connection has already been created for you. The read_products API scope has been selected, and the shopifyProduct
data model imported into your app.
All that needs to be done to complete the connection is to connect to a Shopify Partners app and install it on a development store. After installing, Shopify product data will automatically be synced to your Gadget app's database via a code effect added to the Shopify Shop install
action. You can find this code snippet in shopifyShop/install.js
.
- Go to the Shopify Partners dashboard and create a new app
- Click Create app manually and name your app
- Copy the Client ID and Client secret back to Gadget. You can + Add App Credentials to the Development environment in Gadget, then click Confirm

- Copy the App URL and Redirection URL back to the Shopify Partners dashboard. These can be pasted on the App setup page of your Partners app
- Go back to the Overview page of your Partners app and click Select store to install on a development store
Your app should successfully install on the development store, and a sync of shopifyProduct
data should start automatically.

To view the status of your sync:
- On the Connections page, click Shop Installs
Once your sync is complete, you can build your first quiz in the shop admin.
Making our first Quiz
Now for the fun part, making a quiz!
After installing the app you will be redirected to the main page for the embedded quiz app in the Shopify admin. Go ahead and create a new quiz - add some questions and answers, and link answers to recommended products.
We can look at records in Gadget and see how our frontend app connects with Gadget through the client and makes API calls against it. If we look at the quiz data by selecting the Data label on the quiz
model in the left-hand sidebar, we should see at least one instance of quiz
, including its id, title, and body. We can also inspect our other records to see how our pieces work together to create our quiz experience.
When you've got a quiz that you're happy with, note the id
of the quiz.
Embed your quiz in a Shopify storefront
Installing in a Shopify theme
While we used an NPM package to install our client into our freestanding app, we'll need another method of calling the client in our Shopify shop's theme. Gadget allows us to call our API client directly with a script tag.
We only need the client to run to serve the desired product recommendation quiz. In this case, we'll make a new template for the Page resource and then use it on a page we'll create to hold the quiz.
In your Shopify admin for your shop, head to Online Store > Themes and select Edit Code under the Actions menu for the theme you wish to edit.

Under Sections, create a new section called quiz-page.liquid.
We're going to replace this page with the following code:
html1<link rel="stylesheet" href="{{ 'section-main-page.css' | asset_url }}" media="print" onload="this.media='all'">2<link rel="stylesheet" href="{{ 'component-rte.css' | asset_url }}" media="print" onload="this.media='all'">34<!-- Find your direct script tag in the Installing section in your Gadget project's API Reference -->5<script src="YOUR DIRECT SCRIPT TAG HERE"></script>6<script>7 // create a new instance of the Gadget client at the `api` global connected to the Development environment8 window.api = new Gadget({9 environment: "Development"10 })11</script>1213<script src="{{ 'product-quiz.js' | asset_url }}" defer="defer"></script>14<noscript>{{ 'section-main-page.css' | asset_url | stylesheet_tag }}</noscript>15<noscript>{{ 'component-rte.css' | asset_url | stylesheet_tag }}</noscript>1617<div class="page-width page-width--narrow">18 <product-quiz class="quiz">19 <h1 class="main-page-title page-title h0 product-quiz__title">20 </h1>21 <div class="product-quiz__body">22 <span>23 </span>24 </div>25 <form class="form">26 <div class="product-quiz__questions" id="questions">27 <div class="product-quiz__question">28 <span class="product-quiz__question-answer">29 </span>30 </div>31 </div>32 <hr class="product-quiz__submit-hr" />33 <div class="product-quiz__email-container">34 <label for="email">Enter your email to complete quiz</label><br>35 <input type="email" id="product-quiz__email" name="email" required="required" style="font-size: 16px; height: 32px"><br><br>36 </div>37 <button38 name="quiz-submit"39 type="submit"40 class="product-quiz__submit button button--secondary"41 >42 Get my results43 </button>44 </form>45 </product-quiz>46</div>4748{% schema %}49{50"name": "t:sections.quiz-page.name",51"tag": "section",52"class": "spaced-section"53}54{% endschema %}
We just need to replace the "YOUR DIRECT SCRIPT TAG URL HERE" with your Gadget app's script tag so we can use the client. Your app's script tag URL can be found in the Installing section of your quiz app's API Reference docs.
Your direct script tag should be for your Development environment! Your script tag needs --development added to your app-specific subdomain.
<script src="https://example-app--development.gadget.appapi/client/web.min.js"></script>
When you deploy your Gadget app to Production and set up a quiz on a shopper-facing store, make sure to use the production URL in your script tag.
Now under Templates, select “Add a new template” and add a template called page.quiz.json. This requires you to select the page template type.
Replace the generated file with the following JSON:
1{2 "sections": {3 "main": {4 "type": "quiz-page",5 "settings": {}6 }7 },8 "order": ["main"]9}
Using our client with JavaScript
Under the Assets section in the sidebar, select Add a new asset and create a new JavaScript file called product-quiz.js. You can then add the following to that file:
JavaScript1// initialize an API client object2const api = new Gadget();3const QUIZ_ID = 1; // <- UPDATE ME WITH QUIZ ID FROM GADGET45// query Gadget for the recommended products based on quiz answers, using a JS query6async function fetchRecommendedProducts(answerIds) {7 const queryIdFilter = answerIds.map((answerId) => {8 return { id: { equals: answerId } };9 });1011 const recommendedProducts = await api.answer.findMany({12 filter: {13 OR: queryIdFilter,14 },15 select: {16 recommendedProduct: {17 id: true,18 image: {19 url: true,20 },21 productSuggestion: {22 id: true,23 title: true,24 body: true,25 handle: true,26 },27 },28 },29 });3031 return recommendedProducts;32}3334// fetch the quiz questions and answers to be presented to shoppers, using a GraphQL query35async function fetchQuiz(quizId) {36 const quiz = await api.query(`37 query {38 quiz(id: ${QUIZ_ID}) {39 id40 title41 body42 questions {43 edges {44 node {45 id46 text47 answers {48 edges {49 node {50 id51 text52 }53 }54 }55 }56 }57 }58 }59 }`);6061 return quiz.quiz;62}6364// save the shopper's email and recommended productions to Gadget (for follow-up emails!)65async function saveSelections(email, recommendedProducts) {66 const productsQuery = recommendedProducts.map((rp) => {67 return {68 create: {69 product: {70 _link: rp.recommendedProduct.productSuggestion.id,71 },72 },73 };74 });75 await api.quizResult.create({76 quiz: {77 _link: QUIZ_ID,78 },79 email: email,80 shopperSuggestions: [...productsQuery],81 });82}8384async function onSubmitHandler(evt) {85 evt.preventDefault();8687 const email = document.getElementById("product-quiz__email").value;8889 const submitButton = this.querySelector(".product-quiz__submit");90 submitButton.classList.add("disabled");9192 const recommendedProducts = await fetchRecommendedProducts(selectedAnswers);9394 // save email and recommendations to Gadget for follow-up emails95 await saveSelections(email, recommendedProducts);9697 // display recommendations98 let recommendedProductHTML =99 "<div><h2>Based on your selections, we recommend the following products</h2><div style='display: flex; overflow: auto'>";100101 recommendedProducts.forEach((result) => {102 const { recommendedProduct } = result;103 const imgUrl = recommendedProduct.image?.url;104 const productLink = recommendedProduct.productSuggestion.handle;105 recommendedProductHTML +=106 `<span style="padding: 8px 16px; margin-left: 10px; border: black 1px solid; align-items: center; display: flex; flex-direction: column"><h3>${recommendedProduct.productSuggestion.title}</h3><a class="button" href="/products/${productLink}">Check it out</a>` +107 recommendedProduct.productSuggestion.body +108 `<br/><img src=${imgUrl} width="200px" /><br /></span>`;109 });110111 recommendedProductHTML += "</div></div>";112 document.getElementById("questions").innerHTML = recommendedProductHTML;113114 submitButton.classList.add("hidden");115 this.querySelector(".product-quiz__submit-hr").classList.add("hidden");116 this.querySelector(".product-quiz__email-container").classList.add("hidden");117}118119let selectedAnswers = [];120function selectAnswer(answerId, answerText) {121 selectedAnswers.push(answerId);122 let elId = event.srcElement.id;123 let parent = document.getElementById(elId).parentNode;124 parent.innerHTML = "<h3><b>" + decodeURI(answerText) + "</b> selected</h3>";125}126127fetchQuiz(QUIZ_ID).then(async (quiz) => {128 const questions = quiz.questions.edges;129130 if (!customElements.get("product-quiz")) {131 customElements.define(132 "product-quiz",133 class ProductQuiz extends HTMLElement {134 constructor() {135 super();136 this.form = this.querySelector("form");137 this.heading = this.querySelector(".product-quiz__title");138 this.heading.innerHTML = quiz.title;139 this.body = this.querySelector(".product-quiz__body span");140 this.body.innerHTML = quiz.body;141 this.questions = this.querySelector(".product-quiz__questions");142143 const questionContainer = this.querySelector(".product-quiz__question");144 const answerContainer = this.querySelector(145 ".product-quiz__question-answer"146 );147148 const renderedQuestions = questions.forEach((question, i) => {149 const clonedDiv = questionContainer.cloneNode(true);150 clonedDiv.id = "question_" + i;151 clonedDiv.insertAdjacentHTML(152 "beforeend",153 "<hr /><div><h3>" +154 question.node.text +155 `</h3></div><div class='product-quiz__answers_${i}'></div>`156 );157 this.questions.appendChild(clonedDiv);158159 const answers = question.node.answers.edges;160 answers.forEach((answer, j) => {161 const clonedSpan = answerContainer.cloneNode(true);162 clonedSpan.id = "answer_" + i + "_" + j;163 clonedSpan.insertAdjacentHTML(164 "beforeend",165 `<span><button class="button answer" id="${166 clonedSpan.id167 }" onClick=(selectAnswer(${answer.node.id},"${encodeURIComponent(168 answer.node.text169 )}"))>${answer.node.text}</button></span>`170 );171 this.querySelector(`.product-quiz__answers_${i}`).appendChild(172 clonedSpan173 );174 });175 });176177 this.form.addEventListener("submit", onSubmitHandler);178 }179 }180 );181 }182});
You'll need to make one adjustment here: replace the QUIZ_ID = 0; definition with the ID of the quiz you want to serve. Your Quiz ID can be grabbed from the quiz
model's Data page in Gadget. This JavaScript file includes examples of using the GraphQL API (to fetch the quiz) and the JS API (to fetch recommended products) to make requests to your Gadget backend.
Save your changes, and we're ready to go! Head over to the Pages section of the Shopify admin, and create a new page for your quiz. You can set the template to use your new quiz template. View the page to see your quiz right in your Shopify store, ready to recommend products to your shoppers.
Conclusion
Today, we learned how Gadget and Shopify can work together to create engaging buying experiences for shoppers. This collaboration also offers an easy-to-use platform for building your app in less time. You can expand your app by using the product id to quickly create a shopping cart on the frontend using Javascript, resulting in a faster buying experience. When deploying to Production, remember to update the script tag in your liquid file to match the Production Gadget environment.
Want to know more about building effortless, expressive apps with Gadget? Check out our Guides and get building today!
Need support? Join our Discord, or book office hours with our Solutions team!