David Turton
David Turton

Reputation: 385

How to handle Shopify's API call limit using microapps Node.js module

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

Answers (4)

Anton Swanevelder
Anton Swanevelder

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

Yuci
Yuci

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

noob
noob

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

David Lazar
David Lazar

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

Related Questions