Building a Shopify Admin Panel: A Step by Step Guide
Tags: tutorial
Published: 2021-09-16 08:45:14

*This post has been written by one of Appsmith's contributors, Joseph Petty. This was [previously published here] (https://www.appsmith.com/blog/building-a-shopify-admin-panel-a-step-by-step-guide).*

<hr>

Hey everyone! Joseph here from GreenFlux, LLC. I'm a Shopify Partner Developer and a HUGE fan of Appsmith! 😻

In fact, I've written  [several blog posts](https://blog.greenflux.us/)  on Appsmith integrations already, but today will be my first post officially written for Appsmith! I'm super excited to be working with the Appsmith team and sharing my API integration experience with the community. 

I've done a lot of work with the Shopify API over the last few years, but only recently discovered Appsmith. So I wanted to put together this guide with some specific use-cases that help solve some common business problems. 

*but before I get to those specific use-cases...*
### This guide is written for the Shopify API, but the build process can easily be applied to *any* API. 

The Authentication steps may be different, but most of this guide will work with any API to:
- Perform CRUD *(Create/Read/Update/Delete)* operations with the API directly
- Download/sync API-GET request to Google Sheets
- Find/Modify/Delete a row in Google Sheets based on API data
- Send data from Google Sheets to Shopify *(Create, or Update existing)*

___
### Now for the specific use-cases
Most shops only take orders for products that they have in inventory, then ship them out as the orders come in. *But some businesses wait to begin 'creating' the product until an order has been placed.* Think T-shirt printing, custom-built bikes/skateboards, and even wholesale produce and flowers that are only harvested after an order is placed.

That creation/ harvest process can have several steps and require multiple employees, and even have an approval process with different access levels. Lots of industries are creating products to-order, and need a way to track this process without sharing the Shopify admin login to all users. 

### ☝️ So let's say you have a Shopify store that sells high-tech vehicle upgrades! 🛸 
*And you have a team of workers in three departments:*

`[Assembly, Quality Control, Shipping]` 

|Use Case|Description|
|-|-|
|**Case 1**|Track production with Google Sheets data linked to each order|
|**Case 2**|Safely enable workers to edit orders (mark shipped) without sharing Shopify login|

# Let's get to building!  🛠

Start by creating a Google Sheet for *Workers* and another for *Assignments*. 
Or you can copy this public  [sample sheet](https://docs.google.com/spreadsheets/d/1NN02vjVebOV_Ti3XL7eIfavBX8oIPToqNmtEjo8J4C0/edit?usp=sharing) . 

![2021-08-31 08.33.42.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1630413266830/nZbYdxDlf.gif)

### Next, you'll want to enable the Shopify API. 

1. Login to Shopify and click **Apps** on the left sidebar
2. Scroll down and click *manage private apps*
3. Create a new private app
4. Click *Show inactive Admin API permissions* to expand
5. Select: **Orders** = `Read and write`
6. Save and note the new API Key and Password

Ok, on to Appsmith
___

### Add Shopify Datasource
1. Create a new App (from Drag & Drop)
2. Under Datasources, create a new **Authenticated API**
3. Set the URL to https://`YOUR_STORE_NAME`.myshopify.com/admin/api/`API_VERSION`/
```
https://greenfluxtest.myshopify.com/admin/api/2021-07/
```
4. Set Authentication Type to Basic
5. Enter the API Key (as the username) and the password, and SAVE.
![Screen Shot 2021-08-29 at 9.49.36 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630245034401/EjcizLcXM.png)

### Add Query
Next let's create our first query to GET orders. 
1. Under the Shopify datasource, add a new Query named get_orders
2. Append the URL with `orders.json`
3. Click RUN.
![Screen Shot 2021-08-29 at 9.52.00 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630245167061/KNG8CYVhx.png)

Awesome! Our new app is now making authenticated API calls to Shopify and returning data. 

>Note the response format; All orders are nested inside the "orders" property. 
```
{
  "orders":
    [
      // ARRAY OF JSON OBJECTS
      {Order1},
      {Order2},
      {Order3}...
    ]
}
```

Copy the API and rename `get_products`, then change orders to `products` in the endpoint URL. 

![Screen Shot 2021-08-30 at 5.11.45 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630357938051/f_f487m9w.png)

Next, let's add a few widgets to display the data

### Add Table Widget
1. Click the `Widgets +` on the left sidebar, and drag in a new table widget
2. In the table's settings panel, click the `/` (Slash) command
3. Select the `get_orders` API.
4. Add `.orders` to bind the Table Widget to the order array from response body.
![2021-08-29 09.57.01.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1630245939419/Iak4u1V9Q.gif)

Alright, now to connect the Google Sheet data. 
___
### Add Google Sheets APIs
>If this is your first time connecting Google Sheets to Appsmith, start out by adding a new datasource:

>`Datasources + > Create New > Google Sheets`

 >[Appsmith Docs](https://docs.appsmith.com/datasource-reference/querying-google-sheets) 

>Then follow the prompts and authorize Appsmith to connect to your Google account. 

1. Add new Query under Google Sheets datasource, named `get_workers`
2. Enter URL for your spreadsheet
3. Enter `worker` for the sheet name
4. Set **Row offset** to `0` and **Row Limit** to `50`. 
5. RUN.
![Screen Shot 2021-08-30 at 4.55.38 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630356943282/ZFtv0KaMX.png)

Next, copy the `get_workers` API, rename `get_assignments` and change the sheet name. 

![Screen Shot 2021-08-31 at 6.09.29 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630404574471/yGWQeszcs.png)

Ok, so far we have GET requests working to Shopify and Google Sheets. 

Go ahead and add a few more Table Widgets for Workers and Assignments using the same method above. 

**Now we need a way to feed the get_orders request to the orders sheet.**
___
### Add a POST (Create) API for Google Sheets
Appsmith has a full list of methods available for performing CRUD operations with Google Sheets. We *could* use `Insert sheet row` to add orders one at a time, but the `get_orders` API returns an array of orders. 

Now, the `Bulk Insert Rows` method expects an array of objects, with keys that match the the sheet's column names:

```
[
  {
    header1:value,
    header2:value,
    header3:value
  },
  ...{more objects},
]
```

And the map() method returns an array. 

So we can **map()!** over the `get_orders` data and build our rows.

>Detailed explanation here:
>https://blog.greenflux.us/saving-api-response-data-to-google-sheets-with-appsmith

1. Copy the `get_assignments` API and rename `create_assignments`
2. Change the method to `Bulk insert rows`
3. In the Row Object, enter:
```
{{
get_orders.data.orders.map(
o => {
 return {'id':o.id, 'email':o.email, 'products':o.line_items.map(l => l.name).join()};
}
)
}}
```
4. **RUN** 🚀

![Screen Shot 2021-08-31 at 7.22.49 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630409015023/oGxtsbp9p.png)

## SWEET!
### All open orders are saved to Google Sheets with a single click! 

![giphy (3).gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1630431179275/Jk-faDP7s.gif)

*But what happens when we run it again? *

Uhhh... now I have two copies of every order.

![giphy (2).gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1630431051615/1gF1qK37t.gif)

Hmm... we need a way to tell Shopify what orders we already have, and *only* download new ones!
___

### Passing values from an API response to a new request
Here's where things get interesting. First we have to **get_assignments**, then find the max order number that's already in our sheet, and finally, send a new request to Shopify to get new orders after that order number. 

 [Shopify API docs](https://shopify.dev/api/admin/rest/reference/orders/order#index-2021-07) 

We can use the `since_id` parameter to filter the orders that are returned and then save only new orders back to the sheet. 

First we need to know the last order number from our sheet. I like to store these values into a separate widget, so I can reference just the name, instead of trying to insert a whole function in place of the ID in the URL. 

1. Add a Text Widget named `maxId`
2. Set the Text value to:
```
{{
get_assignments.data.map(
	o =>{ return o.id}
  )
  .sort().reverse()[0]
}}
```
3. Update the `get_orders` API endpoint 

>This will produce an array of all order `id`s, and then sort them in case any of the rows are stored out of order in the sheet. The default sort order is low to high, so reversing and taking the first (zero index [0]) item returns the max value. 

![Screen Shot 2021-08-31 at 8.10.53 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630411857933/NlGNsYjWX.png)

___
**Now we're getting somewhere.** We can continuously download orders to the order sheet, but what about updating individual rows? 

First let's add a few Widgets to view/edit the selected assignment. I've added a Container Widget to the right of the `table_assignments`, Text Widgets to display the Order# and Products, and Input Widgets for the Worker and Instructions. 

![2021-09-02 05.35.38.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1630575599368/5vMlVPvJ4.gif)

Now we can reference the `table_assignments.selectedRow` properties to populate the default values for our Widgets. This will update the Widgets automatically when the selected row is changed. 

![Screen Shot 2021-09-02 at 5.44.49 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630575905300/WUqF5rgYm.png)

Next, let's add a few Widgets to set values in the `assignment` sheet. 

First we want to add a Select Widget (dropdown menu) with all the workers. The `Options` should contain an array of objects with `label` and `value` properties.  (literal array, or expression that returns an array). 

So you could hard-code the Select Widget's Options:
```
[
  {
    'label': 'Worker1',
    'value': W00001'
  },
  {...
  }
}
```
...but wouldn't it be cool if our dropdown could automatically show all the current workers from our workers sheet?! 

```
{{
  get_workers.data.map(
    w =>  {return {label:w.name,value:w.id}
    }
  )
}}
```

![Screen Shot 2021-08-31 at 4.38.57 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630442342981/kVFLS8CkG.png)

![giphy (4).gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1630442686143/yCPE7InyM.gif)

### EXCELLENT! 

Time to test out these new input fields! 
___
### Update a row in Google Sheets
1. Copy the `get_assignments` API and rename `update_assignment`
2. Change the method to `Update a sheet row`
3. Copy a single object from the `get_assignments` response to the `update_assignment` Row Object
4. Add commas between properties
5. Add references to the new Widgets' text values

![Screen Shot 2021-09-02 at 6.18.56 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630577973943/w1hgd8fh9.png)

>NOTE: I removed the `id` and `products` because these should not be edited once downloaded. Our template is only for updating the `worker_id` and associated timestamps/ instructions. 

___
### Fulfill an Order in Shopify

**Ok, we're almost there!** The last piece we need is a way to fulfill an order in Shopify.


>NOTE: Orders can have multiple fulfillments per line item and different status for each fulfillment. 
>To keep this example simple, *our app is going to create a single fulfillment per order* and ignore partial fulfillments. 

This step will be using the `/fulfillments.json` endpoint, and we need the location id. You can get this through the API,  but the easiest way is to view the location and get the id from the URL. 

1. **In Shopify**, click Settings on left sidebar
2. Click Locations
3. Click the location you want to use for fulfillments
4. Copy the id from the URL

We want the employees to be able to select a row in the app and click 'Fulfill Order' without giving them full access to the Shopify admin account. 

In other words, we want to use data from the `selectedRow` of the `table_assignments` to send a POST request to Shopify. 

1. Under the Shopify datasource, add a new Query named `create_fulfillment`
2. Set the URL to `orders/{{Number(table_assignments.selectedRow.id)}}/fulfillments.json`
![Screen Shot 2021-09-02 at 6.23.11 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630578333102/ua3Ok3-JV.png)
3. Set the body to:
```
{
  "fulfillment": {
    "location_id": YOUR_LOCATION_ID,
    "notify_customer": true
  }
}
```
4. Add a button to run the `create_fulfillment` API
5. **MAKE IT SO!** 🛸 


![Screen Shot 2021-09-02 at 6.01.21 AM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1630576905723/y1KjEzEcH.png)
___
### WHEW! What an adventure! 

**Thanks for taking this journey with me on my first official Appsmith post!**

This is just a small sample of the awesome power of Appsmith. There's so much more we could do from here, like connecting another API and sending Slack messages or emails to each worker when an order is assigned, emailing customers pictures of the products during production, or calculating average processing times per worker or product.