Use Gadget to forward Shopify data to an existing service
Gadget simplifies the process of sending Shopify data to an ERP or other existing systems. Integrating with Shopify can be challenging, which is why many builders use Gadget as an in-between step. Whether you need to synchronize data with Shopify, respond to webhooks, or handle authorization, Gadget can serve as middleware to facilitate the seamless movement of data between services.
Before you begin this guide, make sure you have the following:
In this guide, we will use Gadget to build a middleware application that forwards new orders
from Shopify to an existing service. Keep in mind that existing services may have certain restrictions, such as rate limits, that need to be carefully managed. Gadget's built-in background actions can be used to handle these restrictions and ensure that data is forwarded to the existing service reliably.
Step 1: 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 at gadget.new and select the Shopify app template.
To connect your app to a Shopify store, you have two options. The recommended approach that we walk through here is to create a custom app via the Shopify Partners dashboard. Alternatively, you can create a custom application on your Shopify store Admin page.
Now we will set up a custom Shopify application in the Partners dashboard.
- Go to the Shopify Partner dashboard
- Click on the link to the Apps page
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
- Click on Settings in the side nav bar
- Click on Plugins in the modal that opens
- Select Shopify from the list of plugins and connections
- Copy the Client ID and Client secret from your newly created Shopify app and paste the values into the Gadget Connections page
- Click Connect on the Gadget Connections page to move to scope and model selection
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.
- Select the scopes and models listed below and click Confirm to connect to the custom Shopify app
- Select the Orders Read scope and the underlying Order model that you want to import into Gadget
This example uses the Order scope and model, but you can select any Shopify model that you want to import into Gadget. A connection can be edited after it is created, so you can always change scopes and models later.
Now we want to connect our Gadget app to our custom app in the Partner dashboard.
- In your Shopify app in the Partner dashboard, click on Configuration 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
Now we need to install our Shopify app on a store.
- Go back to the Shopify Partner dashboard
- Click on Apps to go to the Apps page again
- Click on your custom app
- Click on Select store
- Click on the store we want to use to develop our app
- You may be prompted about Store transfer being disabled. This is okay, click Install anyway
- Click Install app to install your Gadget app on your Shopify store
If you are getting a permissions denied error when installing your app, try logging in to the Shopify store Admin!
You will be redirected to an embedded admin app that has been generated for you. The code for this app template can be found in web/routes/index.jsx
.
If you are using any of the data models that require access to protected customer data, such as Orders, you need to request access to this data from Shopify. To do this, you can:
- Find the Protected customer data access section on the API access page of your Shopify Partners dashboard
- Click Request access and follow the instructions
Once you have gone through this process, you will be able to properly register webhooks and sync data for the protected customer data models.
For information, such as the models requiring you to fill out the protected customer data access form in Shopify, see our documentation.
Gadget will copy the selected shopifyOrder
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.
Step 2: Create a custom action for forwarding orders
Now that we have connected our Gadget app to Shopify, we can create a custom action to forward order data to an existing service. We create a custom action so we can pass it to the background action API as a parameter.
- Create a new action by adding a file in
api/models/shopifyOrder/actions
calledforward.js
(click + to create a new action) - Paste the following code into
forward.js
:
1import { applyParams, ActionOptions } from "gadget-server";23export const run: ActionRun = async ({ params, record, logger }) => {4 applyParams(params, record);56 // make request to third-party service or ERP7 // in this case, a separate Gadget app is acting as this service8 const response = await fetch("https://orders-request-bin.gadget.app/data", {9 method: "POST",10 headers: {11 "Content-Type": "application/json",12 },13 body: JSON.stringify(record),14 });1516 if (response.ok) {17 const responseMessage = await response.text();18 logger.info({ responseMessage }, "response from service");19 } else {20 // throw an error and the action will be retried by the background action queue21 throw new Error(`ERROR: ${response.status} - ${response.statusText}`);22 }23};2425export const options: ActionOptions = {26 // note the custom action type!27 actionType: "custom",28 triggers: {29 api: true,30 },31};
1import { applyParams, ActionOptions } from "gadget-server";23export const run: ActionRun = async ({ params, record, logger }) => {4 applyParams(params, record);56 // make request to third-party service or ERP7 // in this case, a separate Gadget app is acting as this service8 const response = await fetch("https://orders-request-bin.gadget.app/data", {9 method: "POST",10 headers: {11 "Content-Type": "application/json",12 },13 body: JSON.stringify(record),14 });1516 if (response.ok) {17 const responseMessage = await response.text();18 logger.info({ responseMessage }, "response from service");19 } else {20 // throw an error and the action will be retried by the background action queue21 throw new Error(`ERROR: ${response.status} - ${response.statusText}`);22 }23};2425export const options: ActionOptions = {26 // note the custom action type!27 actionType: "custom",28 triggers: {29 api: true,30 },31};
This action simply uses Node's built-in fetch
call to send data to an external service. If the request is successful, the response is logged. If the request fails, an Error
is thrown and the action will be retried by the background action queue.
Replace the fetch
call with the appropriate method for sending data to your existing service. For example, you might use a client
library to send data to an ERP system.
Request Bin is a free service that can be used to test your actions, and allows you to modify the response, inspect the request, and more.
Next, we can add calls to the forward
action to the background action queue.
Step 3: Add actions to the queue
We want to add our forward
action calls to our background action queue. We can do this from the shopifyOrder
model's create
action. The create
action will be called when a Shopify orders/create
webhook is fired, or a historical data sync is run.
- Paste the following code into
api/models/shopifyOrder/actions/create.js
:
1import { applyParams, save, ActionOptions } from "gadget-server";2import { preventCrossShopDataAccess } from "gadget-server/shopify";34export const run: ActionRun = async ({ params, record }) => {5 applyParams(params, record);6 await preventCrossShopDataAccess(params, record);7 await save(record);8};910export const onSuccess: ActionOnSuccess = async ({ record, api }) => {11 // enqueue the shopifyOrder.forward action, and pass in the id of the record as the custom forward action param12 await api.enqueue(13 api.shopifyOrder.forward,14 { id: record.id },15 {16 // providing a named queue limits max concurrency to 1, only a single queued action runs at a time17 // this helps deal with a rate limit of 10 req/second on the external service18 queue: "ingestion-queue",19 }20 );21};2223export const options: ActionOptions = { actionType: "create" };
1import { applyParams, save, ActionOptions } from "gadget-server";2import { preventCrossShopDataAccess } from "gadget-server/shopify";34export const run: ActionRun = async ({ params, record }) => {5 applyParams(params, record);6 await preventCrossShopDataAccess(params, record);7 await save(record);8};910export const onSuccess: ActionOnSuccess = async ({ record, api }) => {11 // enqueue the shopifyOrder.forward action, and pass in the id of the record as the custom forward action param12 await api.enqueue(13 api.shopifyOrder.forward,14 { id: record.id },15 {16 // providing a named queue limits max concurrency to 1, only a single queued action runs at a time17 // this helps deal with a rate limit of 10 req/second on the external service18 queue: "ingestion-queue",19 }20 );21};2223export const options: ActionOptions = { actionType: "create" };
To add the forward
action to the background action queue, we use the api.enqueue
function. This function takes the action to be enqueued as the first argument, and the parameters to be passed to the action as the second argument. The third parameter defines retry and concurrency settings for background actions. We provide a named queue to limit the maximum concurrency to 1, which helps deal with rate limits on the external service.
Step 4: Test the app
That's all that is required to implement a middleware application that forwards Shopify orders to an existing service.
You can test the app by:
- creating a new order in your Shopify store (go through the checkout process in your connected development store)
- or running a historical data sync in Gadget (Settings > Plugins > Shopify > Installs > Sync)
Click on Queues in the Gadget editor to view your background action queue. You should see all actions that have been added to the queue, whether they are waiting to be run, encountered an error and are retrying, or have run successfully. For more information on the background action queue, see the background actions documentation.
You should see the order data logged in your Gadget Logs for successful action runs, and the data is forwarded to the external service.
Extending this tutorial
In this example, Gadget gives you hands-off data sync and webhook handling with the Shopify connection, while allowing you to continue to use your existing service to manage your store's data.
You can also use background actions to load data in Shopify from one or more external services or implement a two-way sync between systems. Note that this can be any two systems, it doesn't have to be Shopify!
If you have any questions about this guide, feel free to reach out to Gadget's developer Discord.