This is a complementary blog post to a video from theShopifyDevs 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 theleaky 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
今天,我们要带一些practi最好的ces 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 theShopifyDev’s YouTube Channelby 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 readour accompanying tutorial.
We're using GraphQL to control our variables, and itsrequest
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 theapp CLI tooland 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.
Build apps for Shopify merchants
Whether you want to build apps for the Shopify App Store, offer custom app development services, or are looking for ways to grow your user base, the Shopify Partner Program will set you up for success. Join for free and access educational resources, developer preview environments, and recurring revenue share opportunities.
Sign upHow 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 requestproducts(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 asgold
. We’re also asking for theid
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 thevariant
, so we need to take a look at thevariant
associated with that product. Finally, we’re going to issue a request for theID
andweight
, because that's what we're going to use to calculate the new price. We need theweight
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 sayvariants(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)
工作很好。
The above shows that we’re making a request,graphQLClient.request
, with our straightforwardquery
. 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:
您可以看到请求产生了200个不同的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 theproducts
key in JSON, and then we get ouredges
.
Each of theseedges
represents theobject
or thenode
that we're interested in, which happens to be aproduct
. For each of thesenode
s, we have theid
and thevariants
s that are also associated with thatproduct
. 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 arawRequest
.
Issuing arawRequest
to capture metadata and available rate limit
Below our standard request, we have an additional form.
This form uses therawRequest
input, which makes the exact same request with the samequery
, but with some additional information. Instead of getting just a data set back, we're also getting theextensions
,headers
,errors
, andstatus
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 theseproduct
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 productId
s that are returned, and run them with a mutation on them. I need to be able to fetch theproduct
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 anarray
of differentJSON
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常量edges = data.products.edges
. I could even call this常量productedges = data.products.Edges
. That submits a request to look at all that data, which comprises an entireobject
, includingproduct
s, and thenedge
s.
Step 2: Building out ourfor
loop
Now, what we want to do is loop through each one of theseedge
s so that we can access eachproduct
independently. We do that by writing afor
loop.
for (let edge = 0; edge < productEdges.length; edge++)
Thisfor
loop allows us to index into each of the individualarray
s that we get back. But there’s some particular data that we need. So, under ourfor
loop we’re going to write the following code:
常量productId = productEdges[edge].node.id
This is saying that I want theproductId
which is actually equal to (=
) ourproductEdges
. Then I need to index into thenode
key, and then theid
key.
The next thing we’re requesting is thevariantId
, written in the following code:
常量variantId = productEdges[edge].node.variants.edges[0].node.id
This is saying that similarly, we're going to requestproductEdges
, nest again into thenode
, but this time intovariants
andedge
s as well. Keep in mind thatedge
s is still anarray
.
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 singlevariant
, so I'm going to go and grab the first one,[0]
. Finally, we’re going to go into thenode
to get theid
.
The request forvariantWeight
looks similar to the request forvariantId
. It’ll look something like this:
常量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 thatupdatedPrice
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 yourfor
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 ourfor
loop.
Now I’ve created a variable calledproductUpdateMutation
, and I know that within Shopify, this mutation is calledproductUpdate
. It expects an input field of typeProductInput
. It will also return to us theid
of theproduct
that's been updated, because that's what we're requesting.
Fantastic. There we have the mutation.
Creating theproductInput
object
The next thing we need to do is create the input. We can actually take a look in ourproductInput
documentation更多信息在这看什么s like. It outlines all the different input fields that we can get access to, including theid
,metafield
s orpublication
s.
For this example, all we’re interested in is thevariant
s,[ProductVariantIntput ! ]
because that's where the price lives.
Thedocumentationtells us that we’re going to have to provide anid
, avariant
s key, and then the properties associated with the productvariant
. That's going to be anotherid
, this time theprice
key.
Let’s take a shot at implementing this.
Writing yourproductInput
We’ll create an input,常量productInput =
. Then we'll insert anid: productID,
and then we're also going to havevariant
s. Now,variant
is actually anarray
because there are multiple and each of them are a map of their own.
So we’ll putid: variantId,
in that map, and alsoprice: updatedPrice
for this particular edge or product, because we're looping through them all, as outlined above.
Every time we're calculating ourupdatedPrice
here, it's for one particular product and we have that captured within ourproductInput
.
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常量ant 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 themutationData
. 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 theproductInput
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 hitrun. You should expect to see thefor
loop going through each one of thoseproduct
s and making anupdate
to theprice
. 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 ourproductId
back, so that’s fantastic.
Where do we go from here?
现在我们缺少什么?到目前为止,我们不知道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.
Subscribe