I'm developing a simple app which consumes a third-party API (Shopify REST API), the Shopify API is throttled to allow unlimited requests over time though a leaky bucket algorithm which is detailed in their documentation.
I'm using the Shopify-api-node module to make my API calls, it supports an autoLimit
option out of the box which automatically limits the rate in which requests are made so you never have to think about the API call limits.
This works for most Node.js scripts, but in you run into trouble in the context of an Express app where you need to share the API instance across different routes through a session because the autoLimit
option is only reliable with one single Shopify
instance.
Here's my code (notice that I'm creating a new Shopify
instance for each route):
var express = require("express");
var session = require("express-session");
var Shopify = require("shopify-api-node");
var app = express();
var port = 3000;
app.use(session({
secret: "secret",
resave: false,
saveUninitialized: true
}));
app.get("/", (req, res) => {
var shopName = req.session.shopName = req.session.shopName || req.query.shopName;
var apiKey = req.session.apiKey = req.session.apiKey || req.query.apiKey;
var password = req.session.password = req.session.password || req.query.password;
var shopify = new Shopify({ shopName, apiKey, password, autoLimit: true });
shopify.on("callLimits", (limits) => console.log(limits));
var requests = Array.from({ length: 100 }, () => {
return shopify.shop.get()
});
Promise.all(requests)
.then((requestsRes) => {
return res.send(JSON.stringify(requestsRes));
})
.catch((err) => {
return res.status(500).send(err);
});
});
app.get("/other-route", (req, res) => {
var shopName = req.session.shopName = req.session.shopName || req.query.shopName;
var apiKey = req.session.apiKey = req.session.apiKey || req.query.apiKey;
var password = req.session.password = req.session.password || req.query.password;
var shopify = new Shopify({ shopName, apiKey, password, autoLimit: true });
shopify.on("callLimits", (limits) => console.log(limits));
var requests = Array.from({ length: 100 }, () => {
return shopify.product.list()
});
Promise.all(requests)
.then((requestsRes) => {
return res.send(JSON.stringify(requestsRes));
})
.catch((err) => {
return res.status(500).send(err);
});
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}!`)
});
Here's my package.json
:
{
"name": "third-party-api-limit-issue",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.16.4",
"express-session": "^1.15.6",
"shopify-api-node": "^2.18.1"
}
}
When this script is run, go to this URL:
This'll make 100 requests to the Shopify API, store the API credentials in the session and display the shop JSON data.
This breaks if you open the same URL in two tabs at the same time, or open http://localhost:3000/other-route
at the same time. This is happening because I can't figure out how to share the same Shopify
API instance across my routes.
I need to be able to make as many calls to the API across my routes without running into the "Too many requests" error.
Any help would be greatly appreciated.