Reputation: 385
I have been banging my head finding an answer for this I just cant figure out. I am using a Node.js module for the Shopify API by microapps. I have a JSON object containing a list of product id's and skus that I need to update so I looping through the file and calling a function that calls the api. Shopify's API limits calls to it and sends a response header with the value remaining. This node modules provides an object containing the limits and usage. My question is based on the code below how to can at a setTimeout or similar when I am reaching the limit. Once you make your first call it will return the limits object like this:
{
remaining: 30,
current: 10,
max: 40
}
Here is what I have without respecting the limits as everything I tried fails:
const products = JSON.parse(fs.readFileSync('./skus.json','utf8'));
for(var i = 0;i < products.length; i++) {
updateProduct(products[i]);
}
function updateProduct(product){
shopify.productVariant.update(variant.id, { sku: variant.sku })
.then(result => cb(shopify.callLimits.remaining))
.catch(err => console.error(err.statusMessage));
}
I know I need to implement some sort of callback to check if the remaining usage is low and then wait a few seconds before calling again. Any help would be greatly appreciated.
Upvotes: 3
Views: 2414
Reputation: 1155
try this...
const Shopify = require("shopify-api-node");
const waitonlimit = 2;
let calllimitremain = 40;
const shopify = new Shopify({
shopName: process.env.SHOPIFY_URL,
apiKey: process.env.SHOPIFY_KEY,
password: process.env.SHOPIFY_PWD,
autoLimit: true,
});
shopify.on("callLimits", (limits) => {
calllimitremain = limits.remaining;
if (limits.remaining < 10) {
console.log(limits);
}
});
exports.update = async () => {
//Run this before update
while (calllimitremain <= waitonlimit) {
shopify.product.list({ limit: 1, fields: "id, title" });
console.log(`Waiting for bucket to fill: ${calllimitremain}`);
}
//update
await shopify.productVariant.update(
onlineVariantId,
{ compare_at_price: price, price: promo }
);
};
Upvotes: 0
Reputation: 30119
Try making use of the autoLimit
option, for example:
import Shopify from 'shopify-api-node';
const getAutoLimit = (plan: string) => {
if (plan === 'plus') {
return { calls: 4, interval: 1000, bucketSize: 80 };
} else {
return { calls: 2, interval: 1000, bucketSize: 40 };
}
};
const shopify = new Shopify({
shopName: process.env.SHOPIFY_SHOP_NAME!,
apiKey: process.env.SHOPIFY_SHOP_API_KEY!,
password: process.env.SHOPIFY_SHOP_PASSWORD!,
apiVersion: '2020-07',
autoLimit: getAutoLimit(process.env.SHOPIFY_SHOP_PLAN),
});
export default shopify;
According to the library's documentation:
- `autoLimit` - Optional - This option allows you to regulate the request rate
in order to avoid hitting the [rate limit][api-call-limit]. Requests are
limited using the token bucket algorithm. Accepted values are a boolean or a
plain JavaScript object. When using an object, the `calls` property and the
`interval` property specify the refill rate and the `bucketSize` property the
bucket size. For example `{ calls: 2, interval: 1000, bucketSize: 35 }`
specifies a limit of 2 requests per second with a burst of 35 requests. When
set to `true` requests are limited as specified in the above example. Defaults
to `false`.
And this is the version I tried: "shopify-api-node": "^3.3.2"
Regarding the rate limits, refer to Shopify's documentation.
Upvotes: 1
Reputation: 86
I would use something to limit the execution rate of the function used by shopify-api-node
(Shopify.prototype.request
) to create the request, for example https://github.com/lpinca/valvelet.
The code below is not tested but should work. It should respect the limit of 2 calls per second.
var Shopify = require('shopify-api-node');
var valvelet = require('valvelet');
var products = require('./skus');
var shopify = new Shopify({
shopName: 'your-shop-name',
apiKey: 'your-api-key',
password: 'your-app-password'
});
// Prevent the private shopify.request method from being called more than twice per second.
shopify.request = valvelet(shopify.request, 2, 1000);
var promises = products.map(function (product) {
return shopify.productVariant.update(product.id, { sku: product.sku });
});
Promise.all(promises).then(function (values) {
// Do something with the responses.
}).catch(function (err) {
console.error(err.stack);
});
Upvotes: 4
Reputation: 11427
If you look at Shopify code, their github repository has a CLI. That CLI is dealing with the limits. You can quickly learn how Shopify deals with these limits, looking at their code.
Since their code is in Ruby, it is pretty easy to digest. It should not take a skilled JS programmer more than a few minutes to see how to deal with limits based on this code, even abstracting from Ruby.
So my suggestion is to read that Shopify code and try and then morph your JS code to match the same pattern.
Upvotes: -1