This is a complementary blog post to a video from the ShopifyDevs YouTube channel. This is the third post from a four-part series created by Zameer Masjedee, a Solutions Engineer with Shopify Plus.
In the first part of this series, An Introduction to Rate Limits, we covered what exactly an API rate limit is, and how Shopify uses the leaky bucket algorithm. We followed up with a second post, API Rate Limits and Working with GraphQL, to explore the many benefits of GraphQL.
In this post, I'll take all the principles introduced to you in the first two parts of this series, and apply them in a practical application in a development store. First, I'll walk you through what you’ll need to get started. Then, I'll teach you how to make some key real-time requests to the Shopify API.
By the end of this article you'll have a practical understanding of how to make real requests to the Shopify API and how to responsibly consume your rate limit without running into any errors.
Note: All images in this article are hyperlinked to the associated timestamps of the YouTube video, so you can click on them for more information.
What you’ll need to get started
Today, we're going to take some of the best practices and ideas we previously learned and implement them, all while employing some essential business logic. All this will be done in an application that we're going to be working on for a development store. I’ll be using VS Code but feel free to use whatever IDE you like working in.
Our example use case
In the example store that I’ve created, we have a bunch of products that are all associated with a commodity. They're gold bracelets, and as we know, the price of gold fluctuates. In our development store, we create products whenever orders come in, so we need to ensure that we're charging the right amount for them. As the price of gold changes, the price of our products also needs to change. There will be recurring time intervals where we have to take the entire product catalog and update the prices based on the most recent price of gold on the market.
There are a few steps we need to take to achieve this goal. First, we’re going to use a Node application to take a look at the products in my shop that might be impacted by an adjustment in the price of gold. Then, we’re going to implement some best practices in order to update those prices. We’re going to do all this in a way that respects the API rate limit and ensures your app isn't getting throttled.
Before we proceed, If you're new to GraphQL, I'd highly recommend checking out the GraphQL video series on the ShopifyDev’s YouTube Channel by my good friend Chuck Kosman. He walks you through all of those elements, including aliases, variables, and anything that I discuss in this post that might be unfamiliar. You can watch the first video of his series below, or read our accompanying tutorial.
We're using GraphQL to control our variables, and its request
package to be able to make some requests pretty simply.
For this tutorial, I'm not going to build an official Shopify app. That would mean going through the app CLI tool and configuring it that way. We ultimately just want to test it out to see what it would look like if we did have this business logic in our application.
How to retrieve product information
The first thing that we're going to do is make a request. This is demonstrated in the below highlighted query to Shopify.
The request products(first: 200, query: “tag:gold”)
is requesting to retrieve the first 200 products that match our query. In this case, we’re interested in any product that has been tagged as gold
. We’re also asking for the id
of those products. This is an easy way for us to identify the relevant products out of the entire catalog.
We’re interested in adjusting the price, and we know that the price in Shopify lives on the variant
, so we need to take a look at the variant
associated with that product. Finally, we’re going to issue a request for the ID
and weight
, because that's what we're going to use to calculate the new price. We need the weight
to determine what to charge going forward.
Another thing to note is that I'm cheating a little bit in the above example when I say variants(first: 1)
. I know that every single one of my products only has a single variant, because that's how I've set up my store. You might have to play around with a larger number and take a look to see if products have more than one variant. We support up to 100 variants on Shopify, and that would result in some pagination, as you're building a more nested query. For our purposes, variants(first: 1)
works fine.
The above shows that we’re making a request, graphQLClient.request
, with our straightforward query
. We’re going to capture those results (catch((error) => console.log(error))
and print them onto our console, console.log(JSON.stringify(queryData, undefined, 2))
.
Let’s take a look at what happens when I execute this query:
You can see that the request yielded 200 different products. If we run another request for 10 products, it will yield 10 products. It's important to understand that the response we get starts with the products
key in JSON, and then we get our edges
.
Each of these edges
represents the object
or the node
that we're interested in, which happens to be a product
. For each of these node
s, we have the id
and the variants
s that are also associated with that product
. This is the information I need to be able to implement my app.
How to identify the API rate limit of your app
We know from the previous tutorials that there are costs associated with making a query or issuing updates. When doing so, we want to be able to get a sense of how much room we have left in our API rate limit. Shopify will provide this information to you in the form of an extension to your response. The only thing is, we aren’t necessarily able to grab it with the original library above by making a standard request, so we’ll have to issue a rawRequest
.
Issuing a rawRequest
to capture metadata and available rate limit
Below our standard request, we have an additional form.
This form uses the rawRequest
input, which makes the exact same request with the same query
, but with some additional information. Instead of getting just a data set back, we're also getting the extensions
, headers
, errors
, and status
es that are part of that request.
In this example, I'm making the exact same request as above, but also capturing and printing out the available rate limit after this request goes through.
When we issue that request:
We see that we have 958 rate limit points available, in addition to the data we’re capturing. Recall that our bucket fills up at 1,000 points, so we have a lot of room left.
Updating your products with individual mutations
The next step we want to take in our process is to update these product
s. To do so, we’ll make a mutation for each of these products individually.
Remember that when working with GraphQL, you’re charged one point for every nested property or data field that you're capturing. On the other hand, running a mutation costs 10 points, meaning each of these different products is going to cost 10 rate limit points.
You can see how if I'm going to be making a number of requests, I would run through 958 rate limit points pretty quickly.
Recall that we’ve requested our data. The next thing that we’re going to want to do is action it. We want to take every single one of the product Id
s that are returned, and run them with a mutation on them. I need to be able to fetch the product
Id
from the request.
How are we going to do that?
You might also like: An Introduction to Rate Limits
Step 1: Indexing into your array by defining a new variable
We see that our request outputs an array
of different JSON
objects, so we could index into that array. We’re going to do so by defining a new variable.
I'm going to call it const edges = data.products.edges
. I could even call this const productedges = data.products.Edges
. That submits a request to look at all that data, which comprises an entire object
, including product
s, and then edge
s.
Step 2: Building out our for
loop
Now, what we want to do is loop through each one of these edge
s so that we can access each product
independently. We do that by writing a for
loop.
for (let edge = 0; edge < productEdges.length; edge++)
This for
loop allows us to index into each of the individual array
s that we get back. But there’s some particular data that we need. So, under our for
loop we’re going to write the following code:
const productId = productEdges[edge].node.id
This is saying that I want the productId
which is actually equal to (=
) our productEdges
. Then I need to index into the node
key, and then the id
key.
The next thing we’re requesting is the variantId
, written in the following code:
const variantId = productEdges[edge].node.variants.edges[0].node.id
This is saying that similarly, we're going to request productEdges
, nest again into the node
, but this time into variants
and edge
s as well. Keep in mind that edge
s is still an array
.
Ideally, this request would be paginated over a couple of different options, but remember I’m cheating and I know that we only have a single variant
, so I'm going to go and grab the first one, [0]
. Finally, we’re going to go into the node
to get the id
.
The request for variantWeight
looks similar to the request for variantId
. It’ll look something like this:
const variantWeight = productEdges[edge].node.variants.edges[0].node.weight
Now we’ve written in all of our specifications. This allows us to access all the different data points that I know from my objects I wouldn't need to actually implement the mutation. Next, we’re going to look at how to update the price of the new product.
How to update your products prices
There are a few steps involved in responsibly updating the prices of your products, which are outlined below.
First we want to update the price of the product. Updating the price of the product looks like this:
This is basically saying that updatedPrice
is equal to (=
) the weight (variantWeight
) of the product multiplied by (*
) how much it weighs (goldPricePerGram
).
We’re actually going to fix it to two decimal points (toFixed(2)
), representing cents, because Shopify expects a double in terms of the data type in the request input.
Linking the mutation to your for
loop
Next we’re going to have to pull up the mutation to update the price of a product. For simplicity's sake I’ve copied that, so I'm going to insert it into our function and our for
loop.
Now I’ve created a variable called productUpdateMutation
, and I know that within Shopify, this mutation is called productUpdate
. It expects an input field of type ProductInput
. It will also return to us the id
of the product
that's been updated, because that's what we're requesting.
Fantastic. There we have the mutation.
Creating the productInput
object
The next thing we need to do is create the input. We can actually take a look in our productInput
documentation for more information on what this looks like. It outlines all the different input fields that we can get access to, including the id
, metafield
s or publication
s.
For this example, all we’re interested in is the variant
s, [ProductVariantIntput ! ]
because that's where the price lives.
The documentation tells us that we’re going to have to provide an id
, a variant
s key, and then the properties associated with the product variant
. That's going to be another id
, this time the price
key.
Let’s take a shot at implementing this.
Writing your productInput
We’ll create an input, const productInput =
. Then we'll insert an id: productID,
and then we're also going to have variant
s. Now, variant
is actually an array
because there are multiple and each of them are a map of their own.
So we’ll put id: variantId,
in that map, and also price: updatedPrice
for this particular edge or product, because we're looping through them all, as outlined above.
Every time we're calculating our updatedPrice
here, it's for one particular product and we have that captured within our productInput
.
Now we have everything we need to be able to execute this mutation, which is fantastic!
You might also like: How to Build a Shopify App: The Complete Guide.
The final step: calling your mutation
The last thing we want to do is to go ahead and call this mutation. Doing so looks something like this:
To call it, we’re going to say constant mutationData =
I'm going to synchronously wait for the response of the GraphQL client (await graphQLClient.
).
Then we’re going to make a standard request, request(productUpdateMutation, productInput).
.
We also want to be able to catch and log any errors that might happen, catch((error)=> console.log(error))
.
Before running that, you might also want to log the stringified version of the mutationData
. The extra parameters help with formatting and keeping things clean by adding some white space around the new lines. The code for that looks like this:
console.log(JSON.stringify(mutationData, undefined, 2))
We’ve followed the productInput
format laid out in the documentation, however it all needs to be nested within a map, where we specify that it is indeed an input. Otherwise there is confusion around that component. So we’ll just add that in like below:
Once you’ve done that, save and hit run. You should expect to see the for
loop going through each one of those product
s and making an update
to the price
. This is what your terminal should look like:
Success! It's looping through each of our products, retrieving the values, and then it's making that update. We’re also seeing no errors and getting our productId
back, so that’s fantastic.
Where do we go from here?
Now what are we missing? So far, we don't know how much rate limit we still have available when making these requests. We have no idea when we might run into an error. So stay tuned for the next post in this series, where you’ll learn how to capture information, optimize, and responsibly use your app's rate limit.
Build for the world’s entrepreneurs
Want to check out the other videos in this series before they are posted on the blog? Subscribe to the ShopifyDevs YouTube channel. Get development inspiration, useful tips, and practical takeaways.
SubscribeRead more
- The Future of Commerce is Global: Why and How to Take Your Business International with Shopify
- How to Optimize API Rate Limits
- Build for the 20 Percent: How Cleverific Evolved to Meet Merchant Needs
- The Top Trends in the Shopify App Store in 2020 (And What You Can Learn From Them)
- How to Make Your Embedded Apps Load Quickly and Reliably
- How to Build Powerful In-App Surveys: Put the User First
- How We Built Out Our Reporting Dashboard with Shopify Polaris
- 9 App Development Business Trends for 2021 Predicted by Shopify Staff
- 6 Phases of the App Development Process: From Idea to Post-Launch