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.

Requirements

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.

Diagram of the app that is built in the tutorial. There is a box with a Shopify logo with an arrow that represents an orders/create webhook from Shopify. That goes into a Gadget box, which has a shopifyOrder.create action inside. This action has an arrow, labeled api.enqueue, going to a background action queue that handles retires and rate limits. The background action queue box has an arrow, labeled runs, that goes to a shopifyOrder.forward action box, that has a final arrow pointing to an ERP/external service.

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.

  1. Go to the Shopify Partner dashboard
  2. 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 on Apps link in Shopify Partners Dashboard
  1. Click the Create App button
Click on Create app button
  1. Click the Create app manually button and enter a name for your Shopify app
Shopify's app creation landing page in the Partners Dashboard
  1. Click on Settings in the side nav bar
  2. Click on Plugins in the modal that opens
  3. Select Shopify from the list of plugins and connections
The Gadget homescreen, with the Connections link highlighted
  1. Copy the Client ID and Client secret from your newly created Shopify app and paste the values into the Gadget Connections page
Screenshot of the Partners card selected on the Connections page
  1. 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.

  1. 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
Screenshot of the selected Order read and write scopes, and selected Order model
Need different scopes or models?

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.

  1. 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
  2. Copy the App URL and Allowed redirection URL from the Gadget Connections page and paste them into your custom Shopify App
Screenshot of the connected app, with the App URL and Allowed redirection URL(s) fields

Now we need to install our Shopify app on a store.

  1. Go back to the Shopify Partner dashboard
  2. Click on Apps to go to the Apps page again
  3. Click on your custom app
  4. Click on Select store
Click on the Select store button
  1. Click on the store we want to use to develop our app
  2. You may be prompted about Store transfer being disabled. This is okay, click Install anyway
  3. Click Install app to install your Gadget app on your Shopify store
Having an issue installing?

If you are getting a permissions denied error when installing your app, try logging in to the Shopify store Admin!

Click Install app to authorize our Gadget app with our store

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.

Protected customer data access

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.

  1. Create a new action by adding a file in api/models/shopifyOrder/actions called forward.js (click + to create a new action)
  2. Paste the following code into forward.js:
api/models/shopifyOrder/actions/forward.js
JavaScript
1import { applyParams, ActionOptions } from "gadget-server";
2
3export const run: ActionRun = async ({ params, record, logger }) => {
4 applyParams(params, record);
5
6 // make request to third-party service or ERP
7 // in this case, a separate Gadget app is acting as this service
8 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 });
15
16 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 queue
21 throw new Error(`ERROR: ${response.status} - ${response.statusText}`);
22 }
23};
24
25export 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";
2
3export const run: ActionRun = async ({ params, record, logger }) => {
4 applyParams(params, record);
5
6 // make request to third-party service or ERP
7 // in this case, a separate Gadget app is acting as this service
8 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 });
15
16 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 queue
21 throw new Error(`ERROR: ${response.status} - ${response.statusText}`);
22 }
23};
24
25export 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.

Sending data to a service

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.

  1. Paste the following code into api/models/shopifyOrder/actions/create.js:
api/models/shopifyOrder/actions/create.js
JavaScript
1import { applyParams, save, ActionOptions } from "gadget-server";
2import { preventCrossShopDataAccess } from "gadget-server/shopify";
3
4export const run: ActionRun = async ({ params, record }) => {
5 applyParams(params, record);
6 await preventCrossShopDataAccess(params, record);
7 await save(record);
8};
9
10export 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 param
12 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 time
17 // this helps deal with a rate limit of 10 req/second on the external service
18 queue: "ingestion-queue",
19 }
20 );
21};
22
23export const options: ActionOptions = { actionType: "create" };
1import { applyParams, save, ActionOptions } from "gadget-server";
2import { preventCrossShopDataAccess } from "gadget-server/shopify";
3
4export const run: ActionRun = async ({ params, record }) => {
5 applyParams(params, record);
6 await preventCrossShopDataAccess(params, record);
7 await save(record);
8};
9
10export 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 param
12 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 time
17 // this helps deal with a rate limit of 10 req/second on the external service
18 queue: "ingestion-queue",
19 }
20 );
21};
22
23export 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.

Was this page helpful?