How can I execute many request in foreach without error please ?
Currently, I send a request on each entry on my array with foreach :
users.forEach(function (user) {
request({
url : 'myurl.com/api',
method: 'POST',
auth : {
'bearer': CONFIGURATION.auth.token
},
body : {
sender_id: user.sender_id
},
json : true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
resolve(body);
} else {
console.log('Error on coreService');
console.log('############### ERROR ###############');
console.log(error);
console.log('############### BODY ###############');
console.log(body);
console.log('############### RESPONSE ###############');
console.log(response);
reject(error);
}
});
});
With some request it's ok, but with some request I have this error :
Error on coreService
############### ERROR ###############
{ Error: connect ECONNRESET 127.0.0.1:80
at Object._errnoException (util.js:1022:11)
at _exceptionWithHostPort (util.js:1044:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1198:14)
code: 'ECONNRESET',
errno: 'ECONNRESET',
syscall: 'connect',
address: '127.0.0.1',
port: 80 }
############### BODY ###############
undefined
############### RESPONSE ###############
undefined
Do you have an idea how can I fix this problem please ?
I tried with :
server.timeout = 0;
or
server.timeout = 1000;
but same problem...
If I execute the request, user by user, it's fine !
But with the foreach, it's break on some request...
In the comments colinux proposes that the connection reset errors are due to the server protecting itself from too many simultaneous requests, and I think they are probably correct. This code shows how you can use async/await to make your requests to the server one at a time. This might be too slow for you, but it could help you to confirm that the problem is as explained by colinux.
Here is another answer which does not require the use of request-promise-native and instead wraps the request calls in its own Promise.
const request = require('request');
const users = [{sender_id: 1}, {sender_id: 2}, {sender_id: 3}];
// make this driver function async so we can use await which allows waiting for
// a request to finish before starting the next one
async function runUserRequests() {
for (let i = 0; i < users.length; i++) {
const user = users[i];
try {
const response = await requestPromise(user);
console.log("response for user", user, response);
} catch (error) {
console.log("error for user", user, error);
}
};
}
// wrap the request call in a Promise so that it will run synchronously
function requestPromise(user) {
return new Promise(function(resolve, reject) {
request({
url: 'http://localhost:4000/',
method: 'GET', // method 'POST'
// auth : {
// 'bearer': CONFIGURATION.auth.token
// },
// body : {
// sender_id: user.sender_id
// },
// json : true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
resolve(body);
console.log("request successful for user", user, " at ", (new Date()).getTime());
} else {
console.log('Error on coreService');
console.log('############### ERROR ###############');
console.log(error);
console.log('############### BODY ###############');
console.log(body);
console.log('############### RESPONSE ###############');
console.log(response);
reject(error);
}
});
});
}
runUserRequests();
/*
Sample server used to test the code above:
const express = require('express')
const app = express()
const port = 4000
app.get('/', (req, res) => {
console.log("spinning for a bit");
setTimeout( () => {
console.log(" now responding");
res.send('Hello World!');
}, 1000);
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
*/
In the comments colinux proposes that the connection reset errors are due to the server protecting itself from too many simultaneous requests, and I think they are probably correct. This code shows how you can use async/await to make your requests to the server one at a time. This might be too slow for you, but it could help you to confirm that the problem is as explained by colinux.
To get this to work you'll need to install request-promise-native. If you can't do that let me know and I can work up an example wrapping the request api in your own Promise.
const request = require('request-promise-native');
//const users = [1, 2, 3, 4]; // dummy user array for testing
async function runUserRequests(users) {
for (let i = 0; i < users.length; i++) {
const user = users[i];
console.log("starting request for user ", user);
await request({
url: 'http://localhost:4000/',
method: 'GET',
auth : {
'bearer': CONFIGURATION.auth.token
},
body : {
sender_id: user.sender_id
},
json : true
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
console.log("request successful for user", user, " at ", (new Date()).getTime());
resolve(body);
} else {
console.log('Error on coreService');
console.log('############### ERROR ###############');
console.log(error);
console.log('############### BODY ###############');
console.log(body);
console.log('############### RESPONSE ###############');
console.log(response);
reject(error);
}
});
};
}
runUserRequests();
Related
I am trying to develop a google cloud function that will make an external https GET request and return the response body to the client.
Flow:
client makes request to mockServer function
function makes GET request to example.com
function returns "results" from response body from example.com to client
exports.mockServer = (req, res) => {
'use strict';
var https = require('https');
var options = {
host: 'example.com',
path: '/path',
headers: {
'accept': 'application/json',
'X-API-Key': 'XXXX'
}
};
if (req.method == 'GET'){
https.get(options, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
if (res.statusCode === 200) {
var res_body = JSON.parse(data);
var results = JSON.stringify(res_body.result)
console.log("results:"+results);
} else {
console.log('Status:', res.statusCode);
}
});
}).on('error', function (err) {
console.log('Error:', err);
});
} else {
console.log("Wrong Method");
}
res.end()
};
I am able to successfully log the results with console.log("results:"+results); but I cannot figure out how to get it returned to the client. I am still new to this and am learning, so thank you so much in advance to any help!
Posting #YouthDev's solution as an answer:
Thanks to #DougStevenson and #Deko in the comments, I switched to an axios library and it works like a charm. Thank you to both for pointing me in the correct direction. Below is the working axios code.
exports.mockServer = (req, res) => {
const axios = require('axios').create({
baseURL: 'https://example.com'
});
return axios.get('/path',{ headers: {'accept': 'application/json','X-API-Key': 'XXXXXX'} })
.then(response => {
console.log(response.data);
return res.status(200).json({
message: response.data
})
})
.catch(err => {
return res.status(500).json({
error: err
})
})
};
I'm trying to create a firebase function that makes a HTTP POST request whenever a new document is created.
This is my code:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
const request = require("request");
exports.sendMessage = functions.firestore.document('comms/{comms}').onCreate((snap, context) => {
const newValue = snap.data();
if (newValue) {
//const email = newValue.email;
const msg = newValue.msg;
return request({
uri: "url",
method: 'POST',
body: msg,
json: true,
resolveWithFullResponse: true
}).then((response: { statusCode: number; }) => {
if (response.statusCode >= 400) {
throw new Error(`HTTP Error: ${response.statusCode}`);
}
console.log('SUCCESS! Posted', msg);
});
}
return Promise
});
Error received:
TypeError: request(...).then is not a function
at exports.sendMessage.functions.firestore.document.onCreate (/srv/lib/index.js:25:12)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:127:23)
at /worker/worker.js:825:24
at
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
request supports callback interfaces natively but does not return a promise, which is what you must do within a Cloud Function.
This is explained in the official Firebase video series here: https://firebase.google.com/docs/functions/video-series/. In particular watch the three videos titled "Learn JavaScript Promises" (Parts 2 & 3 especially focus on background triggered Cloud Functions, but it really worth watching Part 1 before).
You could use request-promise (https://github.com/request/request-promise) and the rp() method which "returns a regular Promises/A+ compliant promise". You would then adapt your code as follows:
import * as functions from 'firebase-functions';
const admin = require('firebase-admin');
const rp = require('request-promise');
exports.sendMessage = functions.firestore.document('comms/{comms}').onCreate((snap, context) => {
const newValue = snap.data();
if (newValue) {
const msg = newValue.msg;
var options = {
method: 'POST',
uri: '....',
body: msg,
json: true // Automatically stringifies the body to JSON
};
return rp(options)
.then(parsedBody => {
// POST succeeded...
console.log('SUCCESS! Posted', msg);
return null;
})
.catch(err => {
// POST failed...
console.log(err);
return null;
});
} else {
return null;
}
});
request module doesn't return a Promise instead try using a callback function for response.
return request({
uri: "url",
method: 'POST',
body: msg,
json: true,
resolveWithFullResponse: true
}, function (error, response, body) {
})
As in the documentation already mention you need to pass the callback to your request
var request = require('request');
request('http://www.google.com', function (error, response, body) {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
console.log('body:', body); // Print the HTML for the Google homepage.
});
If you want to chain your request you can use pipe
request
.get('url/img.png')
.on('response', function(response) {
console.log(response.statusCode) // 200
console.log(response.headers['content-type']) // 'image/png'
})
.pipe(request.put('url'))
If you want to use promise you can use request-promise
var rp = require('request-promise');
rp('http://www.google.com')
.then(function (htmlString) {
// Process html...
})
.catch(function (err) {
// Crawling failed...
});
The request module work on callbacks only, If you want to make Promisify you need to do like this
const request = require('request');
const webService = {};
webService.callApi = (url, bodyObj, method) => {
return new Promise((resolve, reject) => {
const options = {
method: method || 'POST',
url: url,
headers: {
'Content-Type': 'application/json',
},
body: bodyObj,
json: true,
};
// Error Handler
const errorMessge = { code: 500, error: 'INTERNAL_SERVER_ERROR' };
request(options, (error, response, resBody) => {
if (error) {
return reject(errorMessge);
} else if (response.statusCode !== 200) {
return reject(errorMessge);
}
return resolve(resBody);
});
});
};
module.exports = webService;
I'm new to nodejs and I'm migrating my current API from python to nodejs using express.
What I'm trying to do is to make a request to an external API. I'm pretty sure my API call is right, since I copied from the external API example:
exports.getBalance = function() {
return new Promise(function(resolve, reject) {
var command_url = "/v1/transaction/getBalance";
var full_url = API_URL + command_url;
var nonce = getNonce();
var data = "username=" + convertUsername(API_USER) + "&nonce=" + nonce;
const signature = makeSignature(nonce, data, command_url);
var form = {
username: API_USER,
nonce: nonce
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
var headers = {
"X-API-KEY": API_KEY,
"X-API-SIGN": signature,
"X-API-NONCE": nonce,
"Content-Length": contentLength,
"Content-Type": "application/x-www-form-urlencoded"
};
request(
{
url: full_url,
method: "POST",
headers: headers,
body: formData
},
function(error, response, body) {
if (!error) {
body = JSON.parse(body);
if (response.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
} else {
console.log("error:", error);
reject(error);
}
}
);
});
This is my express route:
routes.post("/balance", mistertango.getBalance);
However, when I try to POST to this route, I don't receive nothing. I use Insomnia to run API tests, so Insomnia keeps running with no response from my express API.
I'd like to know how can I debug my code? I'd like to make an API call using Insomnia to my express API, and check if I'm getting a response from my external API request.
Thanks
mistertango.getBlance returns a Promise but express doesn't handle promises by default. You need to call res.send(data) to actually send a response to the client.
routes.post("/balance", async (req, res, next) => {
try {
const balance = await mistertango.getBalance()
res.send({ balance })
} catch (error) {
next(error)
}
})
Or without async/await:
routes.post("/balance", (req, res, next) => {
mistertango.getBalance()
.then(balance => res.send({ balance }))
.catch(error => next(error))
})
Note 1: You might be able to use res.send(balance) instead of res.send({ balance }) as long as balance is not a number. (Response body cannot be a raw number, so I've wrapped it in an object).
Note 2: In both cases, we have to use .catch or try/catch to handle any errors because express won't handle rejected promises on its own. You can use express-promise-router to fix that!
This lambda function works:
exports.handler = function(event, context, callback) {
const done = (err, res, func) => callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
})
done(null, 'works'))
}
I can hit the API Gateway end point and receive the response message, 'works'.
However, when I add a request call (POSTing a message to Slack) that looks like this:
const request = require('request')
const moment = require('moment-timezone')
exports.handler = function(event, context, callback) {
// the json object is built following these docs:
// https://api.slack.com/docs/message-attachments
const SLACK_WEB_HOOK_URL = process.env.SLACK_WEB_HOOK_URL
const uri = SLACK_WEB_HOOK_URL
const method = 'POST'
const options = {
json,
uri,
method,
}
const slack = (opts) => {
request.post(opts, (err, response, body) => {
if (response.statusCode < 300) {
return 'Success!'
} else {
return 'Err!'
}
})
}
const done = (err, res, func) => callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
})
done(null, slack(options))
}
The server hangs when I hit the API end point ...
And I get a Task timed out after 10.00 seconds error:
{
"errorMessage": "2017-06-23T16:41:21.483Z c385e32e-5832-11e7-8c4f-673b524cf783 Task timed out after 10.00 seconds"
}
But my Slack POST request gets sent and I see the message in the channel.
How do I send the POST request and then wait for it to return, and then exit the lambda function with a custom response message?
Thanks for the help!
Put the callback into the callback of the post method
request.post(opts, (err, response, body) => {
if (response.statusCode < 300) {
return callback(null, {
statusCode: '200',
body: res,
headers: { 'Content-Type': 'application/json' },
});
} else {
return callback(err);
}
});
I have a node server that pulls info from a php server and sends it over where it needs to be through a callback. Once or twice ad day the server gets stuck in an infinite loop and I think it's because I'm not properly handling the connection between the the php and node server. I posted my authentication code below. Anyone have any ideas?
exports.authenticate = function(request, callback) {
var https = require('https');
var options = {
hostname: 'www.mysite.com',
port: 443,
path: '/site/chatauth?id=' + request.sessionID,
method: 'GET',
};
var req = https.request(options, function(res) {
//console.log("statusCode: ", res.statusCode);
// console.log("headers: ", res.headers);
res.on('data', function(d) {
// process.stdout.write(d);
});
});
req.end();
req.on('response', function (response) {
var data = '';
response.setEncoding('utf8');
response.on('data', function(chunk) {
data += chunk;
});
// console.log (request.sessionID);
response.on('end', function() {
try {
callback(JSON.parse(data));
} catch(e) {
callback();
console.log("authentication failed");
}
});
});
};
are you sure authenticate function? Your code is seems to be perfect. There is two thing you have to do
1.Make callback on request error, request timeout and request abort.
2.Get deep insight about your callback, In following code you call same function on throwing exception. Are you sure about this?. This could make possibly loop, if it is again reach "authentication" function.
try {
callback(JSON.parse(data));
} catch(e) {
callback();
console.log("authentication failed");
}
I can't find any errors in your code, but due to emitter/asynchronous way perhaps I didn't catch it completely.
I personally use npm module request (Simplified HTTP request client) w/o any problems so far.
You could easily give it a try:
var request = require('request');
request({
uri: 'https://www.mysite.com/site/chatauth?id=' + request.sessionID,
encoding: 'utf8',
timeout: 20000,
strictSSL: true
}, function (err, response, body) {
if (err || response.statusCode !== 200) {
// some error handling
callback('error');
return;
}
callback(null, JSON.parse(body));
});