One of the most exciting features to come out of Shopify’s Winter 24 Editions is more insight into customer account extensibility. With this new Shopify extension type, developers are able to create apps that support a merchant’s customers’ experience. With a link from the merchant, customers will have access to the order index, order status, and profile pages without requiring any sign-in. Any changes or updates they make to their customer profile will be reflected in the Shopify admin without any action from the merchant.
In this tutorial, we’ll build a customer account extension that allows customers to report problems with orders directly from the order status page. It’s important to note that this is only available on new customer accounts.
- A Shopify Partner account
- Access to Gadget (some functionalities are in beta as of this writing)
- Familiarity with Node.js and React frontend development
- Access to Gadget’s customer account extension beta (ask to join in our developer Discord)
Prefer a video?
The first thing you’ll want to do is set up a new project in Gadget and connect it to Shopify.
For this build, it’s important that you request access to protected customer data, indicating that you require it for app functionality.
After this, you’ll want to install your app on a store. Once installed, to make sure everything is running correctly, we recommend creating a test order to confirm your customer and order data syncs to your Gadget database as expected.
Note: If you want to sync order data older than 60 days, you’ll need special permission from Shopify. There's a unique access scope you’ll need and a checkbox on the partner's dashboard that will allow you to do so.
In order to provide buyers with different options that they can report as a problem, we need to create a custom model. To allow customers to quickly submit issues and help merchants easily resolve them, we’re going to provide a list of options instead of allowing for freeform input.
Next, we’re going to build some backend logic so that when a customer selects an issue with one of our orders, our extension will go ahead and relate the issue to the actual order in our database.
Gadget automatically generates a CRUD API for your app, and by default gives you create, update, and delete actions, as well as a read API, detailed in your app’s API docs. Each action comes with a code file, so you can edit and change them as needed. Actions have two main functions: <inline-code>run<inline-code> and <inline-code>onSuccess<inline-code>. The <inline-code>run<inline-code> function is typically useful for database operations, and <inline-code>onSuccess<inline-code> is great for longer-running actions or side effects. For this extension, we’re going to create a <inline-code>custom<inline-code> action.
This action will allow us to run a custom operation on an individual order. Add a new action called <inline-code>addIssue<inline-code> and set the action type to <inline-code>custom<inline-code>. We’ll add the following code to connect the issue the customer selects with an individual order.
In this code, all the <inline-code>run<inline-code> function is doing is saving the record to my database, preventing cross-shop data access, which means merchants and customers won't be able to see each other's data. So if you are building a public app, data tenancy is handled for Shopify models automatically by Gadget.
The <inline-code>onSuccess<inline-code> function, on the other hand, has changed. We’ve also defined a custom parameter for this custom action called <inline-code>issueId<inline-code>. This will allow us to pass the selected issue from the customer, and it’s being pulled from our parameters object.
We’ve included some comments within the code, which provide an example of what you can do when an issue is submitted, if you’d like to explore more.
Next, we’ll configure the necessary access control and roles for our extension, as we need to define them for both merchants and customers.
To allow merchants to manage these issues, we’ll need an embedded frontend.
To set up your frontend, copy and paste the following into <inline-code>frontend/ShopPage.jsx<inline-code>.
With this, we’ve included an API client that will be unique to your app, and we are importing this into a couple sample pages, like <inline-code>ShopPage.jsx<inline-code>. Because this is a React frontend, we can use state, state hooks, or any React hooks that we desire. We provide some useful React hooks for interacting with your Gadget app's API in the <inline-code>@gadgetinc/react<inline-code> package — you can read more in our docs.
At this point, you’ll want to test that everything works, so go ahead and add a few options to your issues. Make sure to include one that is specific to B2B!
Go ahead and run <inline-code>yarn create @shopify/app<inline-code>. Since Gadget gives you all of the infrastructure you need to run your app, we don’t need to use Remix here. Instead, we will select Start by adding your first extension to create an extension-only Shopify CLI app. Finally, run <inline-code>yarn generate extension<inline-code>.
It’s important to note, you do not want to create your extension as a new app. Instead, we’re going to connect to an existing app, specifically, the Partnera app we’ve just spent all this time building.
In the generated code, you’ll find an extensions folder. You should have a <inline-code>shopify.extension.toml<inline-code> file, which you can use to change your extension settings, a <inline-code>package.json<inline-code> file, and in the <inline-code>src<inline-code> folder, you’ll find your actual extension code.
Go ahead and start your extension using <inline-code>yarn dev<inline-code> and select the same store you installed your app on earlier. Copy and paste the generated URLs to make sure everything runs in the customer account pages as expected.
We’re going to update our <inline-code>.toml<inline-code> file to include an additional menu action modal extension. This will add the widget, the UI, and the card that appears, when a customer needs to select an issue.
Your <inline-code>shopify.extension.toml<inline-code> file should look like this:
Make sure that the <inline-code>network_access<inline-code> flag is enabled and set to true, this will allow us to call our Gadget app’s API. An additional extension target, <inline-code>customer-account.order.action.render<inline-code> has also been added to the default <inline-code>toml<inline-code> config.
Lastly, we need to install our Gadget API client. Start by running <inline-code>yarn add @gadgetinc/react<inline-code> and @gadget-client, and enter the name of your Gadget app. If you check out your extension code again, and look at <inline-code>package.json<inline-code>, you should see that your two Gadget packages are installed.
We’ll also set a custom auth token with a passed-in Shopify session token to just allow us to make secure requests from within this extension.
Note: Custom auth tokens are currently in beta. If you’d like to try them out, you can request access in our Discord.
Now that our API client is installed, we can initialize our API client and add the custom code that powers the extension.
First, create a new file <inline-code>src/api.js<inline-code> and paste the following code. Make sure to use your app’s <inline-code>@gadget-client<inline-code> dependency!
This code initializes a version of your Gadget app’s API client and provides a function that can be called to add the session token to the client, so all calls are authenticated and get granted the <inline-code>shopify-customers<inline-code> role we created earlier.
Next, paste the following code into <inline-code>src/MenuActionExtension.jsx<inline-code>:
This code uses the customer account API to check order fulfillment status and renders a button on the orders page if part of an order has been fulfilled.
Finally, create a new file <inline-code>src/MenuActionModelExtension.jsx<inline-code>. This is the modal that will open when the button from <inline-code>MenuActionExtension.jsx<inline-code> is clicked. Paste the following code:
This code renders a form and makes use of Gadget’s <inline-code>useActionForm<inline-code> hook to manage the selected state and call the <inline-code>addIssue<inline-code> action when a customer submits a problem.
You should be done writing code now. Time to test out our extension.
Run your app using <inline-code>yarn dev<inline-code> and follow the link in Shopify’s developer console back to the customer order page. Click on the Report a problem button to open the modal and select a problem for the order. Not seeing the button? Make sure one of your orders is at least partially fulfilled in the store admin.
Once you have reported a problem, you should see an <inline-code>issue<inline-code> stored on the record of the <inline-code>shopifyOrder<inline-code> data model for which you reported a problem.
That’s it! You've now built a Shopify app using Gadget that allows customers to report problems with their orders directly from the order status page. Extend the app by adding a Twilio or SendGrid integration to notify merchants of problems, or add a new page to the admin app that allows merchants to address any issues.
If you have questions or just want to talk about how awesome building Shopify extensions with Gadget is, you can always join our developer Discord.