meteor-shopify icon indicating copy to clipboard operation
meteor-shopify copied to clipboard

Rate limiting

Open dbnoble opened this issue 8 years ago • 1 comments

In terms of this package, is the rate-limiting global or per-instance? From the way it is described, it sounds like I am expected to pass the backoff rate to the instance.

In terms of Shopify itself, is the rate limit per APP ID + ACCESS TOKEN or per APP ID?

I'm having a problem where some of my users have these apps that update products in bulk, each product that is updated fires an individual webhook request, and the webhook fires a function to get the new product details and update them on our servers. Since the rate-limiting does not appear to be globally aware it throws an error and crashes server.

Should I instead be instantiating api globally on the server and then somehow passing in the current shop and keyset parameters dynamically in each Method function? I was under the impression that I needed to supply those parameters at instantiation so every Method has it's own var api = new Shopify.API(opts)

dbnoble avatar Apr 06 '16 15:04 dbnoble

Hey @dbnoble,

As far as I know, the rate limits from shopify are per app, not per accessToken :(.

I'm sorry that meteor-shopify is crashing for you! It's possible that the rate limiting is just broken (and it might be -- I don't think I ever hit the rate limits, and only have tested with the shopify api simulator in tests/), but hopefully the following will help.

Quick Fix

All of Shopify.API internals are exposed in this.config and this._authenticator. So make your Shopify.API objects as normal, in order to authenticate and stuff, but before you fire any API calls, copy the config and _authenticator to some global Shopify.API and use that. I hope this will fix your issues!

var globalAPI = new Shopify.API({ shop: "non-empty but doesn't matter what you put here" });

WebApp.connectHandlers.use("/yourWebhook", function ShopifyAuthRedirect(req, res) {
    var shop = uri.query.shop;
    var id = uri.query.productId; // I don't know how you choose the product
    // TODO verify signature
    var api = apis[shop];
    if (!api) {
        res.writeHead(401, { });
        res.end("No way!");
        return;
    }

    globalAPI.config = api.config;
    globalAPI._authenticator = api._authenticator;
    var product = globalAPI.getProduct(id);
    // Do your business
});

Proper Solution

I think the "proper" solution would be to do per-api_key rate-limiting, not per Shopify.API rate-limiting.

This can be implemented by:

  • Add api_key param to API.prototype._waitAsync
  • Make an api_key => { lastCallLimit, queueLength } object, rateLimits in that file above _waitAsync
  • In _waitAsync, use lastCallLimit and queueLength from rateLimits instead of from this
  • In the generated code in API.define, updatelastCallLimitinrateLimitsinstead ofthis`

I'm not doing any shopify development at the moment, so feel free to make a pull request! If I need a break or something, I may implement this at some point :).

froatsnook avatar Apr 06 '16 18:04 froatsnook