How to build a custom Shopify dashboard (in no time at all!) with Retool and Gadget
TL;DR: Define a GraphQL Resource in Retool to make requests to a Gadget app and quickly build a simple admin application.
There are some great tutorials floating around the internet showing how easy it is to connect Retool and Shopify. If you don’t need additional data models or processing logic, this is a great way to quickly create user interfaces that simply display information from Shopify.
Inserting Gadget into the mix gives you the ability to turn these Retool interfaces into full-on applications with custom data models and code to power your business logic. Gadget is built on NodeJS and Postgres, and gives your application the additional flexibility and customization that comes with a hosted backend and database. All of a sudden, with minimal code, you end up with a completely custom app that:
- Handles authentication with Shopify
- Syncs data from Shopify stores and handles webhook subscriptions
- Automatically generates an extensible CRUD GraphQL API for data models
- Comes with a no-code UI from Retool
All you need to do is focus on solving your actual business problem - all of the boilerplate and much of the frontend will be abstracted for you.
To get started, you’ll need a Gadget application that is connected to a Shopify store. For my example, I used the Shopify Partners connection. We have instructions on how to set up your connection in just a few steps, so I won’t cover that in this post.
Instead, I’ll focus on the more interesting bits - how to set up a Retool application to query for data in Gadget, and how to embed my Retool application inside a Shopify admin.
My app: approving vendors
My app is simple, I need to manage a list of vendors to be included in a featured section of my storefront. I want to see the vendor names and the number of products associated with each vendor, and need to change the approval status of individual vendors.
To solve this in my Gadget app I have a Vendor model that holds information about my product vendors, including the vendor name, the approval status, a relationship to all the vendor’s products, and calculate the count of products for each vendor. I have also added two additional actions to my Vendor model: approve and revoke. These will be called from my Retool app to change a vendor’s approval status, in addition to a <inline-code>findMany<inline-code> query to get my list of vendors.
I’m going to use Retool to build this frontend and need to be able to query data from my Gadget app. My app frontend is just two tables - one displaying all my vendor information for every vendor, and one that only displays approved vendors. There are a couple of quick things I need to take care of on the Gadget side before I start requesting data from Retool.
Gadget Roles, Permissions, and API Keys
The easiest way to query a Gadget backend from Retool is to add an Authorization header to GraphQL requests in Retool. By default, Gadget apps define API keys for both <inline-code>read<inline-code> and <inline-code>write<inline-code> permissions. This means that anyone using an app’s default API keys can read from and/or write to any resource in the Gadget app. These keys are incredibly powerful and that power is not needed for my app, so I’m going to limit the API that my Retool app is authorized to make requests against.
For my Retool sample, I want to only allow access to specific Gadget API actions. This involves doing two things - creating a new role that only has access to a subset of my API, and generating a new API key for this role.
I added a new <inline-code>Retool Users<inline-code> role in Gadget on the Roles and Permissions page and granted that role permission to read my Vendor model, as well as access to the approve and revoke actions for Vendor.
Now that I have defined my new role, I want to assign it to a new API key. I go to the API Keys page, create a new key, and assign it to the <inline-code>Retool Users<inline-code> role. This is the key I will use in my Authorization header in Retool.
Querying a Gadget app from Retool
Retool allows you to make GraphQL queries. As models and actions are added to a Gadget app, a GraphQL API is automatically generated. The TLDR of this post is “make GraphQL queries from Retool to Gadget”.
Create a GraphQL Resource
To avoid having to redefine and copy-paste my Gadget URL and request headers for each request, I created a new <inline-code>GraphQL Resource<inline-code> in Retool. A defined GraphQL resource means I can define my Gadget API URL and Authorization header in one place and reuse it across multiple requests.
Once I created my resource I was ready to query for some actual data!
If you haven’t used Retool before, there is a wizard that gives a nice guided tour for new users that walks you through building an app. I found it pretty helpful and all I really needed to get up and running.
Use the GraphQL resource to make requests
My app just needs two tables - one that lists all my vendors and the number of products associated with each vendor, and another table of vendors that I have approved. Creating the actual tables in Retool is simple, and only requires a drag and drop to create the table components. The fun part is filling the tables with data from Gadget!
In Retool’s <inline-code>Code<inline-code> toolbar, I added a new <inline-code>Resource query<inline-code> and then selected the resource I created earlier. Like I said before, my Gadget app’s GraphQL endpoint and the Authorization header are both defined in this Resource so we don’t need to redefine them for each request. So all I need to do to fetch data is to fill in my GraphQL query to grab all my vendors:
I called this query <inline-code>get_vendors<inline-code> in Retool:
To pipe my data into my first table, I add this snippet to the table’s data field:
And voila! I can press Run on my query (if I haven’t already) and the table is now displaying my vendor data!
You can find sample GraphQL queries for your app’s models in your Gadget application’s API Reference docs. Click on a model name to find samples for searching, filtering, or any CRUD action.
I can do the same thing for the second table of approved vendors. I need to create a new resource query, but this time, my query payload looks a little different because I need to filter on the approved field:
I also have a variable field for filter: <inline-code>{approved: {equals: true}}<inline-code>
I then add this snippet to the data input for my second table to display my approved vendors:
Update data stored in Gadget
This is all the data I want to see in my app. Now I want to be able to approve or revoke vendors. To do this, we need to create two more resource queries in Retool. These will be mutations that edit the approved field status in Gadget. For example, to approve a vendor I send the following mutation:
Along with an id variable that pulls the id from my table: <inline-code>{{vendor_counts.selectedRow.data.id}}<inline-code>
On my table in Retool I can add a new <inline-code>Button<inline-code> column and then run this query when it is clicked. This will change my vendor’s approval status in Gadget!
But wait, Retool is not up-to-date! I need to refresh to get the latest lists of approved vendors. This is an easy fix - we can add success event handlers to the vendor approval query. This means that each time a vendor is approved, our tables will query for the latest data and our Retool app will stay in sync with our Gadget database!
The revoke action is set up in the same way with the revoke GraphQL mutation called instead.
And we’re done! With very minimal coding I was able to stand up a database and application backend, sync with Shopify, add some custom actions to my data models, and then create a simple frontend to manage these resources. Gadget’s auto-generated GraphQL API and simple yet powerful permission and API key systems pair nicely with Retool’s query resources - all you need to do is write your queries and mutations and you can build a scalable, robust application very quickly.
Have questions or want to see something else built with Gadget and Retool? Feel free to let us know in our developer Discord.