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.

A gif of the completed quiz embedded in a Shopify storefront. There are two questions about dogs, the shopper selects an answer for each question and enters their email, and two product are recommended based on the selected answers.

Before starting this guide you need the following:

Step 1: 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:

Fork on Gadget

Here is a diagram showing an overview of the app's tech stack:

A diagram of the quiz tech stack, including Gadget's DB and API, and the Shopify storefront and admin set up

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.

Here's a diagram to demonstrate what relationships our models will have with each other:

The Model Relationship diagram for our app

You need some models to capture the structure of the product quiz:

  • a quiz has many questions
  • each question can have multiple possible answers
  • each answer has a recommendedProduct that relates to a product in a Shopify store

This data model allows us to recommend a product for each question answered by a shopper.

You also need a model to capture user responses to the quiz:

  • quizResult captures 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 shopperSuggestion model links quizResult records to shopifyProduct records

Keeping product 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.

A screenshot of the data models list in Gadget, will all the quiz models

Roles and permissions 

Our quiz app has two different sets of users: merchants building the quiz, and shoppers who take the quiz.

This means we need to manage permissions for both of these roles - shoppers interacting with the embedded admin app get the shopify-app-user role, and shoppers on the storefront are unauthenticated.

Permissions have already been set for this app:

  • the shopify-app-users role has been granted read and write access to the quiz, question, answer, and recommendedProduct models
  • unauthenticated shoppers can read data from the quiz, question, answer, recommendedProduct, and shopifyProduct models
  • unauthenticated users also need to be able to save their responses and have been granted permission to create new records of the quizResult and shopperSuggestion models

You can view these permissions by clicking on the accessControl folder and then selecting permissions in the left nav.

A screenshot of some of the permissions set for unauthenticated and shopify-app-users roles, including the Recommended Product model, Shopper Suggestion, and Quiz Result. The later two are create-only for unauthenticated users, and the Recommended Product model is read/write access for shopify-app-users and read-only for unauthenticated users

Step 2: 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.

Because the quiz app was forked, the Shopify connection has already been defined: the read_products API scope and required data models have been selected.

To complete the connection, connect to a Shopify Partners app and install it on a development store:

  1. Go to the Shopify Partner dashboard and create a new app
  2. Click Create app manually and name your app
  3. Copy the Client ID and Client secret back to Gadget. You can + Add App Credentials to the Development environment in Gadget, then click Confirm
A gif of the Add App Credentials button for the Development environment being clicked
  1. Copy the App URL and Redirection URL back to the Shopify Partners dashboard. These can be pasted on the Configuration page of your Partners app
  2. Go back to the Overview page of your Partners app and click Select store to install on a development store

After following the prompts from Shopify, your app should be successfully installed on the development store!

A screenshot of a completed connection in Gadget, where the app has been installed on one 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 api/models/shopifyShop/actions/install.js.

To view the status of your sync:

  1. Navigate to the Plugins page, and click Shop Installs underneath the Shopify connection tile

Once your sync is complete, you can build your first quiz in the shop admin.

Step 3: 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 page 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.

Step 4: Embed your quiz in a Shopify storefront 

Now we need to place our quiz in the storefront so buyers can take it and see the product recommendations.

The installation process will be different depending on whether your store's theme is using Online Store 2.0 or an older version. If your theme is using Online Store 2.0, you need to set up a theme app extension that allows merchants to place the quiz on an existing page via the storefront editor.

If the current theme is not on Online Store 2.0, you need to manually add code files to the theme to embed the quiz.

Option 1: Theme app extension setup 

To set up a theme app extension, you need to make use of a Shopify CLI app. We have a forkable app you can use to get started:

  1. Use git to clone the Shopify CLI app
  2. cd to the cloned directory
  3. Run yarn install to install dependencies
  4. Update the direct script tag in extensions/theme-extension/blocks/quiz-page.liquid to include your app's script tag URL
Direct script tag
The script tag URL can be found in the Installing section of your app's API Reference.
Your script tag needs --development added to your app-specific subdomain when working on your Development environment:
<script src="https://example-app--development.gadget.appapi/client/web.min.js" defer="defer"></script>
  1. Run yarn dev to start your app and connect to your existing Partners app and development store
  2. Run yarn deploy to deploy your extension to your Partner app
  3. Copy the SHOPIFY_THEME_EXTENSION_ID from inside the .env file generated when you deploy
  4. Paste SHOPIFY_THEME_EXTENSION_ID into your Gadget app's GADGET_PUBLIC_THEME_EXTENSION_ID environment variable (Settings -> Environment Variables)

You should now be set up and ready to add the quiz theme app extension to the storefront theme.

  1. In the embedded admin app used to build the quiz, copy a dash-separeted (-) quiz id for the quiz you want to embed (example id: my-product-quiz)
  2. Click on the Install tab
  3. Select a theme to install the quiz on
  4. Click Preview in theme at the bottom of the page
  5. Paste your copied quiz id into the Quiz ID field to the right of the theme editor and click Save

Your quiz should now be available in your storefront - try it out!

Option 2: Add Liquid to theme 

Follow these steps to add the quiz to a theme that is not using Online Store 2.0:

  1. In the Shopify admin, head to Online Store > Themes and select Edit Code under the Actions menu for the theme you wish to edit
The Shopify Theme Editor
  1. Under Sections, create a new section called quiz-page.liquid.
  2. Add the following to quiz-page.liquid:
1{% assign quizSlug = section.settings.quiz_slug %}
3<link rel="stylesheet" href="{{ 'section-main-page.css' | asset_url }}" media="print" onload="'all'" />
4<link rel="stylesheet" href="{{ 'component-rte.css' | asset_url }}" media="print" onload="'all'" />
6<!-- Find your direct script tag in the Installing section in your Gadget project's API Reference -->
7<script src="{{ YOUR PROJECT SCRIPT URL }}" defer="defer"></script>
9 window.quizSlug = `{{ quizSlug }}`;
12<script src="{{ 'product-quiz.js' | asset_url }}" defer="defer"></script>
13<noscript>{{ 'section-main-page.css' | asset_url | stylesheet_tag }}</noscript>
14<noscript>{{ 'component-rte.css' | asset_url | stylesheet_tag }}</noscript>
16<div class="page-width page-width--narrow">
17 <product-quiz class="quiz">
18 <h1 class="main-page-title page-title h0 product-quiz__title"></h1>
19 <div class="product-quiz__body">
20 <span> </span>
21 </div>
22 <form class="form">
23 <div class="product-quiz__questions" id="questions">
24 <div class="product-quiz__question">
25 <span class="product-quiz__question-answer"> </span>
26 </div>
27 </div>
28 <hr class="product-quiz__submit-hr" />
29 <div class="product-quiz__email-container">
30 <label htmlFor="email">Enter your email to complete quiz</label><br />
31 <input type="email" id="product-quiz__email" name="email" required="required" style="font-size: 16px; height: 32px" /><br />
32 <br />
33 </div>
34 <button name="quiz-submit" type="submit" class="product-quiz__submit button button--secondary">Get my results</button>
35 </form>
36 </product-quiz>
39{% schema %}
41 "name": "Quiz",
42 "tag": "section",
43 "class": "spaced-section",
44 "settings": [
45 {
46 "id": "quiz_slug",
47 "type": "text",
48 "label": "Enter quiz id"
49 }
50 ]
52{% endschema %}
  1. Replace "YOUR DIRECT SCRIPT TAG URL HERE" with your Gadget app's script tag so we can use the client
Direct script tag
The script tag URL can be found in the Installing section of your app's API Reference.
Your script tag needs --development added to your app-specific subdomain when working on your Development environment:
<script src="https://example-app--development.gadget.appapi/client/web.min.js" defer="defer"></script>
  1. Under Templates, select “Add a new template” and add a template called page.quiz.json. This requires you to select the page template type.
  2. Add the following to page.quiz.json:
2 "sections": {
3 "main": {
4 "type": "quiz-page",
5 "settings": {}
6 }
7 },
8 "order": ["main"]
  1. Under Assets in the sidebar, select Add a new asset and create a new JavaScript file called product-quiz.js
  2. Add the following to product-quiz.js:
1// initialize an API client object
2const api = new Gadget();
4// query Gadget for the recommended products based on quiz answers, using a JS query
5async function fetchRecommendedProducts(answerIds) {
6 const queryIdFilter = => {
7 return { id: { equals: answerId } };
8 });
10 const recommendedProducts = await api.answer.findMany({
11 filter: {
12 OR: queryIdFilter,
13 },
14 select: {
15 recommendedProduct: {
16 id: true,
17 productSuggestion: {
18 id: true,
19 title: true,
20 body: true,
21 handle: true,
22 images: {
23 edges: {
24 node: {
25 source: true,
26 },
27 },
28 },
29 },
30 },
31 },
32 });
34 return recommendedProducts;
37// fetch the quiz questions and answers to be presented to shoppers, using a GraphQL query
38async function fetchQuiz(quizSlug) {
39 const quiz = api.quiz.findFirst({
40 filter: {
41 slug: { equals: quizSlug },
42 },
43 select: {
44 title: true,
45 body: true,
46 questions: {
47 edges: {
48 node: {
49 id: true,
50 text: true,
51 answers: {
52 edges: {
53 node: {
54 id: true,
55 text: true,
56 },
57 },
58 },
59 },
60 },
61 },
62 },
63 });
65 window.quizId =; // save the quiz ID for later use
67 return quiz;
70// save the shopper's email and recommended productions to Gadget (for follow-up emails!)
71async function saveSelections(email, recommendedProducts) {
72 const productsQuery = => {
73 return {
74 create: {
75 product: {
76 _link:,
77 },
78 },
79 };
80 });
81 await api.quizResult.create({
82 quiz: {
83 _link: window.quizId,
84 },
85 email: email,
86 shopperSuggestions: [...productsQuery],
87 });
90async function onSubmitHandler(evt) {
91 evt.preventDefault();
93 const email = document.getElementById("product-quiz__email").value;
95 const submitButton = this.querySelector(".product-quiz__submit");
96 submitButton.classList.add("disabled");
98 const recommendedProducts = await fetchRecommendedProducts(selectedAnswers);
100 // save email and recommendations to Gadget for follow-up emails
101 await saveSelections(email, recommendedProducts);
103 // display recommendations
104 let recommendedProductHTML =
105 "<div><h2>Based on your selections, we recommend the following products</h2><div style='display: flex; overflow: auto'>";
107 recommendedProducts.forEach((result) => {
108 const { recommendedProduct } = result;
109 const imgUrl =
110 recommendedProduct.productSuggestion.images?.edges?.[0]?.node?.source;
111 const productLink = recommendedProduct.productSuggestion.handle;
112 recommendedProductHTML +=
113 `<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>` +
114 `<br/><img src=${imgUrl} width="200px" /><br /></span>`;
115 });
117 recommendedProductHTML += "</div></div>";
118 document.getElementById("questions").innerHTML = recommendedProductHTML;
120 submitButton.classList.add("hidden");
121 this.querySelector(".product-quiz__submit-hr").classList.add("hidden");
122 this.querySelector(".product-quiz__email-container").classList.add("hidden");
125let selectedAnswers = [];
126function selectAnswer(answerId, answerText) {
127 selectedAnswers.push(answerId);
128 let elId =;
129 let parent = document.getElementById(elId).parentNode;
130 parent.innerHTML = "<h3><b>" + decodeURI(answerText) + "</b> selected</h3>";
133document.addEventListener("DOMContentLoaded", function () {
134 var quizSlug = window.quizSlug;
136 fetchQuiz(quizSlug).then(async (quiz) => {
137 window.quizId =;
138 const questions = quiz.questions.edges;
140 if (!customElements.get("product-quiz")) {
141 customElements.define(
142 "product-quiz",
143 class ProductQuiz extends HTMLElement {
144 constructor() {
145 super();
146 this.form = this.querySelector("form");
147 this.heading = this.querySelector(".product-quiz__title");
148 this.heading.innerHTML = quiz.title;
149 this.body = this.querySelector(".product-quiz__body span");
150 this.body.innerHTML = quiz.body;
151 this.questions = this.querySelector(".product-quiz__questions");
153 const questionContainer = this.querySelector(".product-quiz__question");
154 const answerContainer = this.querySelector(
155 ".product-quiz__question-answer"
156 );
158 const renderedQuestions = questions.forEach((question, i) => {
159 const clonedDiv = questionContainer.cloneNode(true);
160 = "question_" + i;
161 clonedDiv.insertAdjacentHTML(
162 "beforeend",
163 "<hr /><div><h3>" +
164 question.node.text +
165 `</h3></div><div class='product-quiz__answers_${i}'></div>`
166 );
167 this.questions.appendChild(clonedDiv);
169 const answers = question.node.answers.edges;
170 answers.forEach((answer, j) => {
171 const clonedSpan = answerContainer.cloneNode(true);
172 = "answer_" + i + "_" + j;
173 clonedSpan.insertAdjacentHTML(
174 "beforeend",
175 `<span><button class="button answer" id="${
177 }" onClick=(selectAnswer(${},"${encodeURIComponent(
178 answer.node.text
179 )}"))>${answer.node.text}</button></span>`
180 );
181 this.querySelector(`.product-quiz__answers_${i}`).appendChild(
182 clonedSpan
183 );
184 });
185 });
187 this.form.addEventListener("submit", onSubmitHandler);
188 }
189 }
190 );
191 }
192 });

Make sure to save changes for each file!

We're ready to test our quiz!

  1. Head over to the Pages section of the Shopify admin (Online Store -> Pages)
  2. Add a new page for your quiz using the quiz theme template

Now you can go back to your theme editor and search for the quiz page to preview your quiz right in the storefront.

  1. In the embedded admin app used to build the quiz, copy a dash-separeted (-) quiz id for the quiz you want to embed (example id: my-product-quiz)
  2. Paste your copied quiz id into the Quiz ID field to the right of the theme editor and click Save

Your quiz should now be available in your storefront - try it out!


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!