Rate limiting request-promise in for loop - node.js

I'm trying to to limit the rate at which I'm firing GET requests to an outside API using promises but I'm having difficulty getting it working. In my scenario, I'm using 'request-promise' module and I need to send GET requests for 175 items from an API (one request for each item ID). The API has a rate limit of 40 requests per 10 seconds so my throttle needs to be 250ms per request. I am trying to send a request inside loop for each item ID, like:
files.forEach(function (file, i) {
console.log("The item ID is " + file.match(re)[1]);
client.send(new APIClient.requests.getItem(file.match(re)[1]))
.then((item) => {
...
})
.catch((error) => {
console.error(error);
// Use fallback
});
...
Here is a snippet of my API client return a request promise (rp) with a 250ms timeout and no callback:
const rp = require('request-promise');
const rp_errors = require('request-promise/errors');
...
send(request, callback) {
...
return rp(options)
.then(this._parseResponse)
.then((response)=> {
return new Promise( (resolve) => setTimeout(function(){
if (callback) { return callback(null, response); }
return resolve(response);
}, 250));
})
.catch(rp_errors.StatusCodeError,((error) => {
throw new errors.ResponseError(request, error.statusCode, error.message);
}
))
.catch(rp_errors.RequestError,((error) => {
if(error.cause.code === 'ETIMEDOUT' || error.cause.code === 'ESOCKETTIMEDOUT')
throw new errors.TimeoutError(request, error);
throw error;
}
))
.catch((error) => {
if (callback) {return callback(error)};
throw error;
});
}
The Async doesnt work and it returns this stack trace of "429 request limit exceeded"
{ ResponseError: 429 - {"status_code":25,"status_message":"Your request count (175) is over the allowed limit of 40."}
[0] at rp.then.then.catch (/mnt/c/Users/ridhwaan/Source/homehost/lib/api-client.js:52:19)
[0] at tryCatcher (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/util.js:16:23)
[0] at /mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/catch_filter.js:17:41
[0] at tryCatcher (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/util.js:16:23)
[0] at Promise._settlePromiseFromHandler (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:512:31)
[0] at Promise._settlePromise (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:569:18)
[0] at Promise._settlePromise0 (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:614:10)
[0] at Promise._settlePromises (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/promise.js:689:18)
[0] at Async._drainQueue (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:133:16)
[0] at Async._drainQueues (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:143:10)
[0] at Immediate.Async.drainQueues [as _onImmediate] (/mnt/c/Users/ridhwaan/Source/homehost/node_modules/bluebird/js/release/async.js:17:14)
[0] at runCallback (timers.js:756:18)
[0] at tryOnImmediate (timers.js:717:5)
[0] at processImmediate [as _immediateCallback] (timers.js:697:5)
[0] name: 'ResponseError',
[0] request:
[0] Movie {
[0] method: 'GET',
[0] path: '/movie/24428',
[0] timeout: 10000,
[0] ensureHttps: false,
[0] external_id: '24428' },
[0] statusCode: 429 }

So the main issue is that array.forEach is a synchronous function and will not wait for client.send to finish.
A solution would be to use bluebird.mapSeries (http://bluebirdjs.com/docs/api/promise.mapseries.html) to map through an array and wait for each iteration to finish.
Also don't forget to return the send-promise, so the mapSeries-function will know when it has resolved, so it knows when to start the next iteration:
bluebird.mapSeries(files, function(file){
return send(...)
}
Last a suggestion would be to replace the whole .then(... setTimeout...) part with just .delay(250). Request-Promise already uses bluebird promises so you can make use of their convenience functions http://bluebirdjs.com/docs/api/delay.html. Delay will automatically resolve the value from the previous promise
return rp(options)
.then(this._parseResponse)
.delay(250)
.catch(...)

Related

Cannot read properties of undefined (reading 'getProvider') in Firebase on NodeJS

I am developping Firebase Cloud Messaging to my Android from NodeJS server.
Receiving notifications from the FCM console page was successful.
And I implemented server code with FCM Docs. (https://firebase.google.com/docs/admin/setup)
But at runtime I get an error :
[0] TypeError: Cannot read properties of undefined (reading 'getProvider')
[0] at Object._getProvider (/Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/#firebase/app/dist/index.cjs.js:274:26)
[0] at getMessagingInSw (/Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/#firebase/messaging/dist/index.sw.cjs:1522:16)
[0] at sendPushNotification (/Users/scmoon/Desktop/work/CheckRefrigerator/server/routes/product.js:86:5)
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/server/routes/product.js:54:9
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/mongoose/lib/model.js:4616:16
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/mongoose/lib/utils.js:264:16
[0] at model.<anonymous> (/Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/mongoose/lib/model.js:472:7)
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/kareem/index.js:315:21
[0] at next (/Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/kareem/index.js:209:27)
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/kareem/index.js:182:9
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/kareem/index.js:507:38
[0] at processTicksAndRejections (node:internal/process/task_queues:78:11)
[0] Emitted 'error' event on Function instance at:
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/mongoose/lib/model.js:4618:13
[0] at /Users/scmoon/Desktop/work/CheckRefrigerator/node_modules/mongoose/lib/utils.js:264:16
[0] [... lines matching original stack trace ...]
[0] at processTicksAndRejections (node:internal/process/task_queues:78:11)
There is no 'getProvider' in my code.
I did googling and tried alternative codes
but it still not working.
here is my code.
...
// const { initializeApp } = require('firebase-admin/app');
// const fbapp = initializeApp();
//const { getMessaging } = require("firebase/messaging");
const { getMessaging } = require("firebase/messaging/sw");
const admin = require("firebase-admin");
const serviceAccount = require('../../server/firebase-adminsdk-asdf.json');
const fbapp = admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
const registrationTokens = [
'ePBbqfWEQ7yaj5Zjxera98...',
// …
];
const message = {
notification: { title: req_body.writer, body: req_body.title },
tokens: registrationTokens,
};
getMessaging(fbapp)
// .sendMulticast(message)
.sendAll([message])
.then((response) => {
console.log(response.successCount + ' messages were sent successfully');
});
Not a firebase expert but it seems that sendAll messages expect a token property without a s while sendMulticast accepts tokens.
Which actually makes sense after reading the docs:
token to send each message to 1 target
tokens to send each message to n targets

creating custom token in cloud function after reteiving data from realtime database

i want to create custom token in cloud function but before that I want to check and compare timestamp from realtime database and with current time.if the timestamp is below 10 min then I want to create custom token and send back to client.please help me to achieve this.i am new to this cloud function firebase.
here is my code
export const authaccount = functions.https.onCall(async (data) => {
try {
const snap= await admin.database().ref("/register/"+data).get();
const time=snap.val().timestamp;
const now=new Date().getDate();
const reg=new Date(time).getDate();
const today=Math.abs(now-reg);
const daydiff=Math.floor(today/1000/60/60/24);
const nowminutes=new Date().getUTCMinutes();
const regminutes=new Date(time).getUTCMinutes();
const timediff=Math.abs(nowminutes-regminutes);
if (timediff<10 && daydiff==0) {
try {
admin.auth().createCustomToken(data).then((customtoken)=>{
console.log("auth created"+" "+timediff+" "+daydiff+" "+customtoken);
return customtoken;
});
} catch (err1) {
throw new functions.https.HttpsError("unknown", err1.message, err1);
}
} else {
console.log("else "+" "+now+" "+reg+" "+time+" "+daydiff);
}
} catch (err2) {
throw new functions.https.HttpsError("unknown", err2.message, err2);
}
});
2:53:20.626 AM
authaccount
Error: Process exited with code 16 at process.<anonymous> (/layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/invoker.js:275:22) at process.emit (events.js:314:20) at process.EventEmitter.emit (domain.js:483:12) at process.exit (internal/process/per_thread.js:168:15) at Object.sendCrashResponse (/layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/logger.js:37:9) at process.<anonymous> (/layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/invoker.js:271:22) at process.emit (events.js:314:20) at process.EventEmitter.emit (domain.js:483:12) at processPromiseRejections (internal/process/promises.js:209:33) at processTicksAndRejections (internal/process/task_queues.js:98:32)
2:53:19.559 AM
authaccount
Error: The caller does not have permission; Please refer to https://firebase.google.com/docs/auth/admin/create-custom-tokens for more details on how to use and troubleshoot this feature. at FirebaseAuthError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:44:28) at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:90:28) at new FirebaseAuthError (/workspace/node_modules/firebase-admin/lib/utils/error.js:149:16) at Function.FirebaseAuthError.fromServerError (/workspace/node_modules/firebase-admin/lib/utils/error.js:188:16) at /workspace/node_modules/firebase-admin/lib/auth/token-generator.js:114:53 at processTicksAndRejections (internal/process/task_queues.js:97:5) at async Promise.all (index 1)
2:53:19.558 AM
authaccount
Unhandled rejection
2:53:19.469 AM
authaccount
Function execution took 1386 ms, finished with status code: 200
please help to solve this problem. i don't know where I am making the mistake.
You will need to make sure your Firebase Admin sdk is initiated and running before the function proceeds
if (firebase.apps.length === 0) {
firebase.initializeApp();
}
Resource: https://firebase.google.com/docs/admin/setup#initialize-without-parameters
I doubt you have modified the IAM permissions on your service account but as the comment suggested: https://firebase.google.com/docs/auth/admin/create-custom-tokens#service_account_does_not_have_required_permissions
Once that is confirmed to be working - you will need to ensure that the onCall data is a string and not null, some simple health checks can help you debug your process
console.log(typeof data);
console.warn("Data", data);
from there I would also debug your date times and the realtime database result, these are async and will require the promise to be resolved before you can use it.
Update:
All cloud functions should return a response to the client
onCall uses promises on the client and supports a 'return Object'
example:
return {
token: myCustomToken,
possible: otherValue
};
for comparison, onRequest uses fetch like responses and supports codes
response.status(500)
response.send({name:value})
return;
Source:
https://firebase.google.com/docs/functions/callable#sending_back_the_result
Source:
https://firebase.google.com/docs/functions/http-events#using_express_request_and_response_objects
Update:
all paths and promises need to resolve correctly, this includes awaiting promises to resolve and returning their result or storing the result for any secondary processing - I suggest cleaning up the code, remove the try/catch and use .then().catch()
Example:
if (timediff<10 && daydiff==0) {
return await admin.auth().createCustomToken(data)
.then((customtoken)=>{
console.log("auth created"+" "+timediff+" "+daydiff+" "+customtoken);
return customtoken;
})
.catch (err) {
return new functions.https.HttpsError("unknown", err.message, err);
}
}
else {
console.log("else "+" "+now+" "+reg+" "+time+" "+daydiff);
return "else "+" "+now+" "+reg+" "+time+" "+daydiff;
}

Chat application like Slack tutorial

I am following this tutorial here: https://www.youtube.com/watch?v=a-JKj7m2LIo
I got stuck around the 14 minute mark getting the following error message in the terminal:
(node:6248) UnhandledPromiseRejectionWarning: RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: undefined
[0] at ServerResponse.writeHead (_http_server.js:237:11)
[0] at ServerResponse._implicitHeader (_http_server.js:228:8)
[0] at write_ (_http_outgoing.js:616:9)
[0] at ServerResponse.end (_http_outgoing.js:733:5)
[0] at ServerResponse.send (D:\htdocs\mern\react-slack-clone\node_modules\express\lib\response.js:221:10)
[0] at ServerResponse.sendStatus (D:\htdocs\mern\react-slack-clone\node_modules\express\lib\response.js:359:15)
[0] at D:\htdocs\mern\react-slack-clone\server.js:31:13
[0] at processTicksAndRejections (internal/process/task_queues.js:93:5)
[0] (node:6248) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise
which was not handled with .catch(). (rejection id: 1)
[0] (node:6248) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero
exit code
Still learning how to use React and Node, but I think the error is occurring in my server.js file.
My server.js code looks like this:
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const Chatkit = require('pusher-chatkit-server')
const app = express()
const chatkit = new Chatkit.default({
instanceLocator: 'fhdsakfjsdalkfjdsalfjsdlajflsad',
key:
'ruewioqruewfhdsakljfdsaljfsdlakjds483294'
})
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use(cors())
app.post('/users', (req, res) => {
const { username } = req.body
chatkit
.createUser({
name: username,
id: username
})
.then(() => res.sendStatus(201))
.catch(error => {
console.log('Error:', error) // updated
/*if(error.error_type === 'services/chatkit/user_already_exists') {
res.sendStatus(200)
} else {
res.sendStatus(error.statusCode).json(error)
}*/
})
})
const PORT = 3001
app.listen(PORT, err => {
if (err) {
console.error(err)
} else {
console.log(`Running on port ${PORT}`)
}
})
Basically, when I submit a name, in the console's network tab, I should see the post, but I am not. The error message is all I get but I'm not sure how to decipher it.
Edit
Added console.log('Error:', error) to the code above and received the following error message in the terminal:
Error: ErrorResponse {
[0] status: 404,
[0] headers: {
[0] 'access-control-expose-headers': 'X-Envoy-Upstream-Service-Time,
Server, Access-Control-Expose-Headers, Access-Control-Max-Age, Date',
[0] 'access-control-max-age': '86400',
[0] 'content-type': 'application/json',
[0] date: 'Tue, 17 Mar 2020 18:07:17 GMT',
[0] server: 'istio-envoy',
[0] 'x-envoy-upstream-service-time': '10',
[0] 'content-length': '209',
[0] connection: 'close'
[0] },
[0] error: 'not_found',
[0] error_description: 'Not found',
[0] error_uri: 'https://docs.pusher.com/errors/not_found'
[0] }
I do not understand what this error message means. I check the URL provided by error_uri, and the message on that page read, "The server couldn't find the requested resource. Please make sure that the name of the resource you are requesting is correct." I am not entirely sure what that means.
Edit 2
I made the following change to app.post in an attempt to produce another terminal error, as follows:
app.post('/users', (req, res) => {
const { username } = req.body
chatkit
.createUser({
name: username,
id: username
})
.then(() => res.sendStatus(201))
.catch(error => {
//console.log('Error:', error)
if(error.error_type === 'services/chatkit/user_already_exists') {
res.sendStatus(200)
} else {
//res.sendStatus(error.statusCode).json(error)
res.sendStatus(500)
console.log(JSON.stringify(error))
}
})
})
I received the following response in the terminal:
{"status":404,"headers":{"access-control-expose-headers":"Date,
X-Envoy-Upstream-Service-Time, Server, Access-Control-Expose-Headers,
Access-Control-Max-Age","access-control-max-age":"86400",
"content-type":"application/json","date":"Thu, 09 Apr 2020 02:34:22
GMT","server":"istio-envoy",
"x-envoy-upstream-service-time":"11",
"content-length":"209","connection":"close"},
"error":"not_found","error_description":
"Not found","error_uri":"https://docs.pusher.com/errors/not_found"}
I do not know what any of this means.

Node Express js Unhandled rejection Error [ERR_HTTP_HEADERS_SENT]

I am new for node js. I am trying to insert the data to database, before that I did some validation for checking username already exist in database. Please see the coding which is used by me.
var express = require("express");
var router = express.Router();
var models = require("../models");
const { check, validationResult } = require('express-validator/check');
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource - merchant');
});
/* POST ADD USERS. */
router.post('/add-merchant', [
check('name').not().isEmpty(),
], (req, res, next) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.json({ errors: errors.array()});
}
console.log("error passed")
models.merchants.findOne({ where: {name: req.body.name}}).then((merchant) => {
if(merchant) {
throw new Error("Username already exists")
}
models.merchants.create(req.body).then((merchant) => {
res.json(merchant)
});
})
} catch (error) {
res.json({"status": "error", "message": error.message})
}
});
module.exports = router;
I am getting below error while running the above code
Unhandled rejection Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (/home/assistanz/Desktop/node/gb360/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/home/assistanz/Desktop/node/gb360/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/home/assistanz/Desktop/node/gb360/node_modules/express/lib/response.js:267:15)
at models.merchants.create.then (/home/assistanz/Desktop/node/gb360/routes/merchant.js:33:21)
at tryCatcher (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/promise.js:512:31)
at Promise._settlePromise (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/promise.js:569:18)
at Promise._settlePromise0 (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/promise.js:614:10)
at Promise._settlePromises (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/promise.js:694:18)
at _drainQueueStep (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/async.js:138:12)
at _drainQueue (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/async.js:131:9)
at Async._drainQueues (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/async.js:147:5)
at Immediate.Async.drainQueues [as _onImmediate] (/home/assistanz/Desktop/node/gb360/node_modules/bluebird/js/release/async.js:17:14)
at runCallback (timers.js:705:18)
at tryOnImmediate (timers.js:676:5)
at processImmediate (timers.js:658:5)
Please help anyone to solve this issue.
Thanks in advance.
this error occurs because you return the response and something is remain to load
you have to remove console.log("error passed") or you can put it before return statement..
and write return statement for every res.json statement
as
return res.json({})
HTTP uses a cycle that requires one response per request. When the client sends a request the server should send only one response back to client. So modify the code as below,
models.merchants.findOne({ where: { name: req.body.name } }).then((merchant) => {
if (merchant) {
throw new Error("Username already exists")
} else {
models.merchants.create(req.body).then((merchant) => {
res.json(merchant)
});
}
})

Google Trends API npm quota exceeded

I am using Google Trends API to get historical trends data. I am reading keywords from a CSV file and firing google trends query.
Here is the code
var googleTrends = require('google-trends-api');
var fs = require('fs')
fs.readFile('merged.csv', 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
data = data.toString().split("\n");
recur(0, data);
});
function recur(index, data){
if (index < data.length){
var keyword = data[index].split(",")[0];
console.log(keyword);
googleTrends.trendData(keyword)
.then(function(results){
console.log(results);
index = index+1;
recur(index, data);
})
.catch(function(err){
console.error('We have an error!', err);
});
}
}
I get this error many times in between
[ Error: Quota limit exceeded, try again later
at parseJSON (/Users/shubhamjindal/Development/domain-info/node_modules/google-trends-api/lib/resources/htmlParser.js:48:9)
at /Users/shubhamjindal/Development/domain-info/node_modules/google-trends-api/lib/utils/trendData.js:26:11
at tryCatcher (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/promise.js:504:31)
at Promise._settlePromise (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/promise.js:561:18)
at Promise._settlePromise0 (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/promise.js:606:10)
at Promise._settlePromises (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/promise.js:685:18)
at Async._drainQueue (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/async.js:138:16)
at Async._drainQueues (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/async.js:148:10)
at Immediate.Async.drainQueues (/Users/shubhamjindal/Development/domain-info/node_modules/bluebird/js/release/async.js:17:14)
at runCallback (timers.js:570:20)
at tryOnImmediate (timers.js:550:5)
at processImmediate [as _immediateCallback] (timers.js:529:5) ]
Can someone suggest me some workaround this error?

Resources