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 

To learn how to create a Shopify connection, follow the Shopify quickstart guide. Note that you may also use the assistant to create a new Shopify connection.

For this tutorial, we will need the read_orders scope and the order model.

Screenshot of the selected Order read and write scopes, and selected Order model
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?