Close

Sign up to Gadget

Start for free

How to build a bundle app on Shopify (with Shopify Functions)

Riley Draward
October 25, 2022

Time to build: ~1.5 hours

Building product bundling applications for Shopify stores is a complex task - you need an app in the Shopify admin for bundle management, a place to store and manage all of your bundle information, and changes to the Shopify storefront so the bundle information is correctly presented to shoppers. Bundling products together are a great way to increase storefront revenue, and Gadget's low-code BaaS (Backend-as-a-Service) platform makes it simple to set up a backend that manages all your bundle data.

Requirements
Before starting this tutorial you need the following:
-> A Shopify Partners account
-> A development store with Checkout Extensibility developer preview enabled, and some sample products added

Prefer a video? Follow along to build the bundle tutorial by creating a Gadget app, getting the Admin app running, and deploying a function.

In this tutorial, you will use Gadget to build an application that can create bundles in the Shopify admin and manages your bundle information that is needed when making storefront enhancements. The provided admin app also includes a Shopify Function extension that will apply bundle discounts to your cart's line items.

You can fork this Gadget project and try it out yourself.
The admin app + Function extension is available in our examples repo on Github.
You will still need to set up the Shopify Connection after forking. Continue reading to learn how to connect Gadget to Shopify!
Fork on Gadget

Bundle app inspiration: Brooklinen

Brooklinen's bundle app is used as inspiration for this tutorial. In this style of bundle app, Storefront changes are made so that it looks like only a single bundle product is added to the cart. In reality, individual line items for bundled products are added to the cart. This isn't the only way to build a product bundle application. This method was chosen for this tutorial because it allows you to track both individual product inventory and the number of times a bundle has been sold.

This tutorial does not include storefront changes so when testing you will add products to your cart individually. Once all products that are part of your bundle are included, the discount will be applied.

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.

To connect our Gadget backend to a Shopify store you have two options. The recommended option if you are new to Gadget and Shopify is to create a custom app via the Shopify Partners dashboard. You can also create a custom application on your Shopify store Admin page. Both of these types of custom apps have a slightly different workflow for connecting, and are detailed below:

Connect to Shopify through the Partners dashboard

Requirements
To complete this connection, you will need a Shopify Partners account as well as a store or development store

Our first step is going to be setting up a custom Shopify application in 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
  • Copy the API key and API secret key from your newly created Shopify app and paste the values into the Gadget connections page

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 and Product variant models that we want to import into Gadget
  • You also need to enable the read and write scopes for the Shopify Discount API. You do not need any Discount models.
  • Click Confirm at the bottom of the page

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
  • Click Connect on the Gadget Connections page to move to scope and model selection

Gadget will copy 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.

Custom bundle models

You need to add custom models to keep track of our bundle information. A custom Bundle model can store the following information:

  • a title
  • a percentage discount
  • a tracking product so that sales can easily be determined
  • relationships to products and product variants that make up the bundle
  • additional information such as an image for the bundle

An intermediate model, Bundle Elements, will be used to track information about products and variants that are part of a bundle. This will be built in Gadget using a HasManyThrough relationship. Each Bundle Element will contain:

  • the price of this bundle element
  • the quantity of this element included in a single bundle
  • relationships to a single product and variant that this bundle element includes in the parent bundle
  • a relationship to the bundle containing this element

Add Bundle Element model

You need to create a new Bundle Element model to store data relating to individual items in a bundle.

  • Click on + to add a new model
  • Name your new model Bundle Element
  • Add a new Number field and name it Quantity, set the Default Value to 1, and add a Required validation

The relationship fields needed will be added in the next step.

Add Bundle model

You need to create a new Bundle model and its required fields in your Gadget app.

  • Click on + to add a model
  • Name your model Bundle
  • Add a new String field, name it Title, and add a Required and Uniqueness validation
  • Add a new Number field, name it Discount, add a Default Value of 0, a Required validation, and a Number Range validation with a minimum of 0 and a maximum of 100
  • Add a new File field and name it Image
  • Add a BelongsTo relationship field so that Bundle belongs to the Shopify Product model, name this field Tracker Product
  • Add a HasManyThrough relationship field so that Bundle has many Shopify Products through Bundle Element, name this field Products.
  • Name the relationship fields on Bundle Element Product on the Shopify Product side of the relationship and Bundle on the Bundle side of the relationship
  • Add another HasManyThrough relationship field so that Bundle has many Shopify Product Variants through Bundle Element, name this field Variants
  • Name the relationship fields on Bundle Element Product Variant on the Shopify Product Variant side of the relationship and Bundle on the Bundle side of the relationship

Now you have all the custom models needed to store bundle information in Gadget. There is one final step before you set up your admin app - you need to run custom code after a new Bundle is successfully created.

Run code on a Bundle create action

When a new bundle is created in Gadget, you need to save a tracker product back to Shopify, so the bundle can be surfaced in your store. You can run a custom Code Effect to write this new product, along with metafield data containing a reference to the Bundle Id, back to Shopify. To write this product back to a store:

  • Go to the Behaviour page for the Bundle model
  • Click the Create action tile
  • Add a Success Effect
  • Select Run Code Snippet
  • Give your new code file the name createBundleProductShopify.js
  • Click the Go to file button to open the code editor
  • Paste this snippet into the editor

In addition to adding tracker product metafield info and linking tracker products to your bundles, the code effect will also:

  • Query all bundle information to be serialized and sent to the Shopify Function so discounts can be applied
  • Update our Shopify API to 2022-10 so that we can use Shopify's Discount API
  • Create a new Discount
  • Store the bundle's structure as JSON within a metafield on the created discount

Set up the admin app

There is a sample bundle admin app that can be used to create new bundles. To get a copy of this project locally, run:


The bundle admin app is a Shopify CLI 3.0 app using Shopify Polaris components and the provided Gadget React tooling. It also has a Functions extension that will be used to apply discounts in your store's cart.

Before you run the admin app, you need to make changes so that your Gadget app is used as the backend.

Update your App URL

To use your local application as your admin app, you need to update the App URL for your connection. Your App URL should look like https://<your-app-slug>.gadget.app/shopify/install.

  • Go to the Connections page in Gadget
  • Click on the Edit button for your connected app
  • Change the App URL to https://localhost

You need to make a similar change to the App URL on the Partners dashboard.

  • Navigate to your app in the Partners dashboard
  • Click on App setup
  • Change the App URL field to https://localhost

These are all the changes that need to be made in Gadget and the Shopify Partners dashboard. Now you need to update the admin app to use your Gadget client.

Update admin app

You need to update the admin app so that you are using your project's Gadget client. These instructions can also be found in the sample admin app's README.

  • Register the Gadget NPM registry with your local environment

  • Remove the sample client from the web/frontend directory. You may need to run npm install or yarn first!

  • Install your client in the web/frontend directory

  • In web/frontend/api/gadget.ts, update the import statement at the top to use your Gadget client

Also, update the @gadget-client import statements to use your gadget app slug in

  • web/frontend/components/AddProducts.tsx
  • web/frontend/components/VariantDetails.tsx
  • web/frontend/pages/create-bundle.tsx

You also need to add your Shopify Partners app's API key as an environment variable.

  • Go to your app on the Partners dashboard
  • Copy the API key
  • Create a .env file at the root of your admin app if it doesn't already exist
  • Enter SHOPIFY_API_KEY=<shopify-api-key> into the .env file

Deploy the Shopify Function

You now need to deploy the Shopify Function included in the admin app's extensions folder. Functions allow you to run custom backend logic in Shopify's environment to apply custom discounts. You will use this Function to apply your product bundle discount to line items in your cart.

Shopify Functions need to be written in a language that compiles down to a WebAssembly .wasm file. Shopify is primarily using Rust for their Functions tutorials and examples, but you can use any language that compiles down to .wasm. The sample Function extension in the admin app is written in AssemblyScript and looks similar to TypeScript.

Shopify Functions work by reading cart and discount metafield data from STDIN, applying custom logic to apply the desired discounts, and writing a result to STDOUT. The provided Function has 3 main code files located in extensions/bundle-tutorial-function/assembly:

  • index.ts: the primary file for the Function that reads from STDIN, applies discount logic, and writes the results to STDOUT
  • api.ts: contains classes for the Functions Product Discount API required for this tutorial
  • metafieldValue.ts: contains classes for the discount metafield that will be passed in your Function

The values being passed into your Function are declared in input.graphql.

You need to deploy the Function extension to your Partners app:

  • Build your Function locally to install the AssemblyScript compiler, navigate to extensions/bundle-tutorial-function and run npm install or yarn
  • From the root level of your admin app, run npm run deploy or yarn deploy

The Function will then run the build process specified inside the shopify.function.extension.toml file, which compiles the AssemblyScript down to a .wasm file. Once your Function is deployed, you should be able to see it in your app's Extensions in the Partners dashboard.

Once your Function is deployed, we need to make some additional changes to the Gadget app so that we can pass bundle information into the Function.

Update Gadget app

When you deployed your Function, a FUNCTION_ID was written in your app's .env file, for example SHOPIFY_BUNDLE_TUTORIAL_FUNCTION_ID=<your-key>. We need to copy this value and add it to our Gadget app as an Environment Variable.

  • Click on Environment Variables in the left navbar
  • Click Add Variable in the top right corner of the window
  • Set the variable key to FUNCTION_ID
  • Copy the value from your admin app's .env file and paste as the variable value in Gadget

You also need to add the metafield namespace and key as environment variables in Gadget. These values can be found in your input.graphql file that is in your Function extension.

First, add the metafield key:

  • Click Add Variable
  • Set the variable key to METAFIELD_KEY
  • Set the variable value to function-config

Then, add the metafield namespace:

  • Click Add Variable
  • Set the variable key to METAFIELD_NAMESPACE
  • Set the variable value to product-bundles-gadget

Update model permissions

If you try to run your admin app and create a bundle you will be greeted by a GGT_PERMISSION_DENIED error! We need to grant admin app users permission to our Bundle and Bundle Element models in Gadget.

  • Click Roles and Permissions in the left navbar in Gadget
  • Under the Shopify App Users role, enable the Read and Write permission for both the Bundle and Bundle Element models

Run the admin app and create a bundle

Everything is now set up to be able to run your admin app and create a new bundle in your store Admin!

  • Start your local admin app with yarn dev
  • If prompted to create a new Partners app, or connect to an existing app, select connection an existing app and select your Partners app created at the beginning of this tutorial
  • Through the Partners dashboard, select a development store with Checkout Extensibility preview enabled on which to install your app
  • Sync your products and product variants to Gadget by going back to your Connections page in Gadget, clicking Shop Installs for your connection, and then Sync for the installed store
  • Go to the store on which you have installed your app and click on Apps in the sidebar
  • Select your app from the command palette
  • Click Create a new bundle to build a new bundle

On the bundle creation page, you can add a title for your bundle, upload an image, and enter a discount percentage for all products in your bundle. If you click Add product you will be able to select a product and the product variants you want to add as options to your bundle. You can also choose the quantity of this product included in a single bundle.

Finally, clicking Save bundle will save your new bundle to your Gadget application's database! If you go back to your Gadget application and look at the Data pages for the Bundle and Bundle Elements models, you will see the newly created bundle data.

You should also see a Discount created in your Shopify Store. You can verify by going to your store's Admin and clicking on Discounts in the left navbar.

Test a bundle purchase

If your Bundle discount appears, you are ready to test the Function!

View your online store in Shopify and add the products contained in your bundle to your cart.

You need to add the individual products in the correct quantity to your cart, not the bundle tracker product!

Once you have added all the products contained in your bundle to your cart, you can view the cart. The items in your cart will be passed to your deployed Function. If the cart contains all items included in the bundle, a bundle discount will be applied to those cart items.

Congrats, you have built a product bundle application using Gadget, Shopify CLI 3.0, Shopify Functions, and AssemblyScript!

Next steps

Now that you have a sample bundle app set up, you can go about customizing it to suit your needs.

Features that could be good to implement on top of this tutorial:

  • Allow for bundle editing in the admin app
  • Use the Bundle Update action to update metafield information
  • Handle bundle deletion
  • On the Bundle Remove action, delete or deactivate the relevant Discount
  • You may need to store the Discount id as a new field on the Bundle model

Some resources to check out include:

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