making an https request inside of an azure function - javascript

I'm attempting a simple request:
var options = {
host: 'hookb.in',
path: '/8PMoEa9kbaCXgXYxOmdr5',
method: 'POST'
};
var req = http.request(options, (res) => {
var body = context.bindingData.name;
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
});
}).on("error", (error1) => {
context.log('error');
context.res = {
status: 500,
body: error1
};
});
req.end();
context.done();
However, there's no response (and no request received by the target here https://hookbin.com/8PMoEa9kbaCXgXYxOmdr).
What am I doing wrong? Is there a special way to create an https request inside of an azure function?
var Jimp = require("jimp");
var http = require('https');
module.exports = async function (context, myBlob) {
context.log("JavaScript blob trigger function processed blob \n Name:", context.bindingData.name, "\n Blob Size:", myBlob.length, "Bytes");
context.log(process.env.ImageConvertedWebHook);
Jimp.read(myBlob, function (err, image) {
image.getBufferAsync(Jimp.MIME_TIFF, function (error, imageData) {
context.log('Node.JS blob trigger function resized ' + context.bindingData.name + ' to ' + image.bitmap.width + 'x' + image.bitmap.height);
context.bindings.outputBlob = imageData;
var options = {
host: 'hookb.in',
path: '/8PMoEa9kbaCXgXYxOmdr5',
method: 'POST'
};
var req = http.request(options, (res) => {
var body = context.bindingData.name;
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
});
}).on("error", (error1) => {
context.log('error');
context.res = {
status: 500,
body: error1
};
});
req.end();
context.done();
});
});
};
I've also attempted this way:
const data = 'buy milk biotch';
var options = {
host: 'hookb.in',
path: '/8PMoEa9kbaCXgXYxOmdr',
method: 'POST',
port: 443,
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length
}
};
const req = https.request(options, res => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
context.log(d)
})
})
req.on('error', error1 => {
context.log(error1)
})
req.write(data)
req.end()

This is a working example of how to request Azure AD v2 endpoint to get access token inside of your Azure Function V3 (node runtime)
var http = require('https');
module.exports = function (context, req) {
var body = "";
body += 'grant_type=' + req.query['grant_type'];
body += '&client_id=' + req.query['client_id'];
body += '&client_secret=' + req.query['client_secret'];
body += '&code=' + req.query['code'];
const options = {
hostname: 'login.microsoftonline.com',
port: 443,
path: '/ZZZZZZZZ-bc69-4c8b-8e91-11f3a181c2bb/oauth2/v2.0/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': body.length
}
}
var response = '';
const request = http.request(options, (res) => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', (d) => {
response += d;
})
res.on('end', (d) => {
context.res = {
body: response
}
context.done();
})
})
request.on('error', (error) => {
context.log.error(error)
context.done();
})
request.write(body);
request.end();
};
Differences between this and OP's: function is not marked as async (module.exports = function) and I'm using context.done(); to let the runtime know when an async action (https request in our case) is done. context.done(); is in two places: 'end' and 'error' callbacks.
I believe async/await + promises should be used instead of callbacks if you want to use an async function - link
Using the async and await keywords helps avoid both of these errors.
You should use the Node.js utility function util.promisify to turn
error-first callback-style functions into awaitable functions.
Not a JS dev.

I had the same issue but after removing async from module.exports = async function (context, myBlob), this should be work. If you want to treat this as a async function, this might be helpful.

Seems like naming conflict issue .Couple of variables to change in your code:
Change below
var http = require('https');
To
var httpMod = require('https');
Change below
const req = https.request(options, res => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
context.log(d)
})
})
To
const customReq = httpMod.request(options, res => {
context.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
context.log(d)
})
})
Hope it helps.

Related

My weather app is throwing type error of temp.. It was console logging the data when I checked but as I enter city Lat and Log it shows type error [duplicate]

I'm trying to get my function to return the http get request, however, whatever I do it seems to get lost in the ?scope?. I'm quit new to Node.js so any help would be appreciated
function getData(){
var http = require('http');
var str = '';
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
callback = function(response) {
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
console.log(str);
});
//return str;
}
var req = http.request(options, callback).end();
// These just return undefined and empty
console.log(req.data);
console.log(str);
}
Of course your logs return undefined : you log before the request is done. The problem isn't scope but asynchronicity.
http.request is asynchronous, that's why it takes a callback as parameter. Do what you have to do in the callback (the one you pass to response.end):
callback = function(response) {
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
console.log(req.data);
console.log(str);
// your code here if you want to use the results !
});
}
var req = http.request(options, callback).end();
Simple Working Example of Http request using node.
const http = require('https')
httprequest().then((data) => {
const response = {
statusCode: 200,
body: JSON.stringify(data),
};
return response;
});
function httprequest() {
return new Promise((resolve, reject) => {
const options = {
host: 'jsonplaceholder.typicode.com',
path: '/todos',
port: 443,
method: 'GET'
};
const req = http.request(options, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var body = [];
res.on('data', function(chunk) {
body.push(chunk);
});
res.on('end', function() {
try {
body = JSON.parse(Buffer.concat(body).toString());
} catch(e) {
reject(e);
}
resolve(body);
});
});
req.on('error', (e) => {
reject(e.message);
});
// send the request
req.end();
});
}
Shorter example using http.get:
require('http').get('http://httpbin.org/ip', (res) => {
res.setEncoding('utf8');
res.on('data', function (body) {
console.log(body);
});
});
from learnyounode:
var http = require('http')
http.get(options, function (response) {
response.setEncoding('utf8')
response.on('data', console.log)
response.on('error', console.error)
})
'options' is the host/path variable
from learnyounode:
var http = require('http')
var bl = require('bl')
http.get(process.argv[2], function (response) {
response.pipe(bl(function (err, data) {
if (err)
return console.error(err)
data = data.toString()
console.log(data)
}))
})
I think it's too late to answer this question but I faced the same problem recently my use case was to call the paginated JSON API and get all the data from each pagination and append it to a single array.
const https = require('https');
const apiUrl = "https://example.com/api/movies/search/?Title=";
let finaldata = [];
let someCallBack = function(data){
finaldata.push(...data);
console.log(finaldata);
};
const getData = function (substr, pageNo=1, someCallBack) {
let actualUrl = apiUrl + `${substr}&page=${pageNo}`;
let mydata = []
https.get(actualUrl, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', async () => {
if (JSON.parse(data).total_pages!==null){
pageNo+=1;
somCallBack(JSON.parse(data).data);
await getData(substr, pageNo, someCallBack);
}
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
getData("spiderman", pageNo=1, someCallBack);
Like #ackuser mentioned we can use other module but In my use case I had to use the node https. Hoping this will help others.
This is my solution, although for sure you can use a lot of modules that give you the object as a promise or similar. Anyway, you were missing another callback
function getData(callbackData){
var http = require('http');
var str = '';
var options = {
host: 'www.random.org',
path: '/integers/?num=1&min=1&max=10&col=1&base=10&format=plain&rnd=new'
};
callback = function(response) {
response.on('data', function (chunk) {
str += chunk;
});
response.on('end', function () {
console.log(str);
callbackData(str);
});
//return str;
}
var req = http.request(options, callback).end();
// These just return undefined and empty
console.log(req.data);
console.log(str);
}
somewhere else
getData(function(data){
// YOUR CODE HERE!!!
})

How do I add a body to a POST request in JavaScript using a promise?

I have the following function that works fine for a GET request:
function httpGet(options) {
return new Promise(((resolve, reject) => {
const request = https.request(options, (response) => {
response.setEncoding('utf8');
let returnData = '';
response.on('data', (chunk) => {
returnData += chunk;
});
response.on('end', () => {
resolve(JSON.parse(returnData));
});
response.on('error', (error) => {
reject(error);
});
});
request.end();
}));
}
Using the following options this works fine:
let path = `/1/indexes/combined-faq-directions?query=${location}&typoTolerance=false&getRankingInfo=true&optionalWords=${location}`;
var options = {
host: 'YQRKWVXBWV-1.algolianet.com',
port: 443,
path: path,
method: 'GET',
headers: {
"X-Algolia-API-Key": "APIKEYXXXX",
"X-Algolia-Application-ID": "IDXXX"
}
};
But I'm now trying to do a POST to a different URL using the same format. Through postman I can see this works fine but I can't seem to get it into the correct format for my promise.
var https = require('follow-redirects').https;
var fs = require('fs');
var options = {
'method': 'POST',
'hostname': 'api.airtable.com',
'path': '/v0/appasoP3zaNL9k8Pj/Analytics',
'headers': {
'Authorization': 'Bearer keyxxxx',
'Content-Type': 'application/json',
'Cookie': 'brw=brw9wuY1b9RmtTl1q'
},
'maxRedirects': 20
};
var req = https.request(options, function (res) {
var chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.on("end", function (chunk) {
var body = Buffer.concat(chunks);
console.log(body.toString());
});
res.on("error", function (error) {
console.error(error);
});
});
var postData = JSON.stringify({"records":[{"fields":{"RoomNo":"Room 6"}}]});
req.write(postData);
req.end();
I have tried this to adjust it but getting an error ERR_STREAM_WHITE_AFTER_END
let path = `v0/appasoP3zaNL9k8Pj/Analytics`;
var options = {
host: "api.airtable.com",
port: 443,
path: path,
method: "POST",
headers: {
Authorization: "Bearer keyxxx",
"Content-Type": "application/json",
},
};
var postData = JSON.stringify({ records: [{ fields: { RoomNo: "Room 6" } }] });
async function asyncCall() {
const response = await httpGet(options, postData);
console.log(response);
}
asyncCall();
function httpGet(options, postData) {
return new Promise((resolve, reject) => {
const request = https.request(options, (response) => {
response.setEncoding("utf8");
let returnData = "";
response.on("data", (chunk) => {
returnData += chunk;
});
response.on("end", () => {
resolve(JSON.parse(returnData));
});
response.on("error", (error) => {
reject(error);
});
if (postData) {
request.write(postData);
}
});
request.end();
});
}
I think I'm very close but just can't figure out the syntax. Any help greatly appreciated!

Can we use async/await in cloud functions in firebase?

I have to functions called: getMatchDataApi() and saveApiDataToDb(). getMatchDataApi() function returns value from an api and saveApiDataToDb() function is used to store getMatchDataApi() value into firestore database.
function getMatchDataApi() {
var options = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/scorecards.php?seriesid=2141&matchid=43431",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
json: true
};
var req = http.request(options, res => {
var chunks = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
playerName = json.fullScorecardAwards.manOfTheMatchName;
console.log("player name", playerName);
});
});
req.end();
}
async function saveApiDataToDb() {
await getMatchDataApi();
var name = playerName;
console.log("Aman Singh", name);
}
Here i am using async function. So that first i want it should execute this getMatchDataApi() first and returns the value and after that it should print value inside this function saveApiDataToDb().
And then i am calling saveApiDataToDb() as follow:
exports.storeMatchData = functions.https.onRequest((request, response) => {
saveApiDataToDb()
});
Yes, you can use async/await in cloud functions. But, you can't access/fetch the data outside the google servers in the Spark Plan (Free Plan).
Hope this helps.
Modify your functions/index.js file like this way:
const functions = require('firebase-functions');
const request = require('request');
exports.storeMatchData = functions.https.onRequest( async (req, res) => {
let body = '';
await getMatchDataApi().then(data => body = data).catch(err => res.status(400).end(err));
if (!body) {
return res.status(404).end('Unable to fetch the app data :/');
}
// let json = JSON.parse(body);
// playerName = json.fullScorecardAwards.manOfTheMatchName;
// console.log("Aman Singh", playerName);
res.send(body);
});
function getMatchDataApi() {
const options = {
url: 'https://dev132-cricket-live-scores-v1.p.rapidapi.com/scorecards.php?seriesid=2141&matchid=43431',
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
};
return cURL(options);
}
function cURL(obj, output = 'body') {
return new Promise((resolve, reject) => {
request(obj, (error, response, body) => {
if (error)
reject(error);
else if (response.statusCode != 200)
reject(`cURL Error: ${response.statusCode} ${response.statusMessage}`);
else if (response.headers['content-type'].match(/json/i) && output == 'body')
resolve(JSON.parse(body));
else if (output == 'body')
resolve(body);
else
resolve(response);
});
});
}
I try to solve my issue using promise in cloud functions. so it could help someone.
This is my cloud function
exports.storeMatchData = functions.https.onRequest((request, response) => {
a().then(
result => {
saveApiDataToDb(result);
},
error => {}
);
});
This is the function from which i am calling api and resolving its data first what i want
var options = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/scorecards.php?seriesid=2141&matchid=43431",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
},
json: true
};
var options1 = {
method: "GET",
hostname: "dev132-cricket-live-scores-v1.p.rapidapi.com",
port: null,
path: "/matches.php?completedlimit=5&inprogresslimit=5&upcomingLimit=5",
headers: {
"x-rapidapi-host": "dev132-cricket-live-scores-v1.p.rapidapi.com",
"x-rapidapi-key": "63e55e4f7fmsh8711fb1c0bd9ec2p1d8b4bjsne2b8db0a1a82"
}
};
var a = function getMatchDataApi() {
// Return new promise
return new Promise((resolve, reject) => {
// Do async job
let firstTask = new Promise((resolve, reject) => {
var req = http.request(options, res => {
var chunks = [];
var arr = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
const playerName = json.fullScorecardAwards.manOfTheMatchName;
resolve(playerName);
});
});
req.end();
});
let secondTask = new Promise((resolve, reject) => {
var req = http.request(options1, res => {
var chunks = [];
var arr = [];
res.on("data", chunk => {
chunks.push(chunk);
});
res.on("end", () => {
var body = Buffer.concat(chunks);
var json = JSON.parse(body);
const playerName = json;
resolve(playerName);
});
});
req.end();
});
Promise.all([firstTask, secondTask]).then(
result => {
resolve(result);
},
error => {
reject(error);
}
);
});
};
This is the function in which I am going to use getMatchDataApi() values after resolving in this function.
function saveApiDataToDb(data) {
console.log("Name of player", data[0]);
}

In azure functions (js) POST request after async/await call does not work

I am currently working with azure functions in javascript. In my function, I am first getting a specific element from my CosmoDB (this is the async/await part). I get a result and then I want to do an https POST request. However, my problem is, that it never finished the HTTPs request and I don't really know why. What am I doing wrong?
(As you can see I tried 2 different ways of doing the request, once with the standard https function and the commented out the part with npm request package. However, both ways won't work).
Here is my code:
const CosmosClient = require('#azure/cosmos').CosmosClient;
var https = require('https');
var http = require('http');
var request = require('request');
const endpoint = "someEndpoint";
const masterKey = "anymasterkey";
const database = {
"id": "Database"
};
const container = {
"id": "Container1"
};
const databaseId = database.id;
const containerId = container.id;
const client = new CosmosClient({
endpoint: endpoint,
auth: {
masterKey: masterKey
}
});
module.exports = function (context, req) {
const country = "de";
const bban = 12345678;
const querySpec = {
query: "SELECT * FROM Container1 f WHERE f.country = #country AND f.bban = #bban",
parameters: [{
name: "#country",
value: country
},
{
name: "#bban",
value: bban
}
]
};
getContainers(querySpec).then((results) => {
const result = results[0];
context.log('here before request');
var options = {
host: 'example.com',
port: '80',
path: '/test',
method: 'POST'
};
// Set up the request
var req = http.request(options, (res) => {
var body = "";
context.log('request');
res.on("data", (chunk) => {
body += chunk;
});
res.on("end", () => {
context.res = body;
context.done();
});
}).on("error", (error) => {
context.log('error');
context.res = {
status: 500,
body: error
};
context.done();
});
req.end();
// request({
// baseUrl: 'someURL',
// port: 443,
// uri: 'someuri',
// method: 'POST',
// headers: {
// 'Content-Type': 'text/xml;charset=UTF-8',
// 'SOAPAction': 'someaction'
// },
// function (error, response, body) {
// context.log('inside request')
// if (error) {
// context.log('error', error);
// } else {
// context.log('response');
// }
// }
// })
})
};
async function getContainers(querySpec) {
const {container, database} = await init();
return new Promise(async (resolve, reject) => {
const {
result: results
} = await container.items.query(querySpec).toArray();
resolve(results);
})
}
async function init() {
const {
database
} = await client.databases.createIfNotExists({
id: databaseId
});
const {
container
} = await database.containers.createIfNotExists({
id: containerId
});
return {
database,
container
};
}
The last thing that happens is the print of "here before request". After that the function just does nothing until it timesout. So what am I doing wrong? Can't I just this combination of await/async and requests?
As commented you are not sending any data to the POST call. You need to have a req.write before the req.end
req.write(data);
req.end();
That is why the POST call is failing for you. After this fix, it should work

How to export my GET response from REST API in Node.js?

Below is code for getresponse.js
var https = require('https');
function httpGet(callback){
var body = "";
var issueDesc = [];
var options = {
hostname:'<hostname>'
port:443,
path:'/rest/api/2/search?jql=project=X',
method:'GET',
headers: {
'Authorization':'Basic '+ new Buffer("xxxxx"+':'+"xxxxxx").toString('base64'),
'Content-Type':'application/json',
}
};
https.get(options, function(res, callback) {
res.on('data', function(data) {
body += data;
});
res.on('end', function(callback) {
Response = JSON.parse(body);
var i=Response.total;
for(j=0;j<i;j++){
issueDesc.push(Response.issues[j].key +": "+Response.issues[j].fields.summary +"\n");
}
issueDesc = issueDesc.join("");
callback(issueDesc);
});
});
};
module.exports.httpGet = httpGet;
Below is my code for app.js (controller file)
var getissue = require('./getresponse.js');
getissue.httpGet(function(response) {
console.log(response);
// ....
});
I want to access the issueDesc in app.js but i am getting error as callback is not a function.
Do i have to send the issueDesc as global variable through module.exports?
getresponse.js file
module.exports = {
httpGet: () => {
var https = require('https');
return new Promise((resolve, reject) => {
var body = "";
var issueDesc = [];
var options = {
hostname: '<hostname>'
port: 443,
path: '/rest/api/2/search?jql=project=X',
method: 'GET',
headers: {
'Authorization': 'Basic ' + new Buffer("xxxxx" + ':' + "xxxxxx").toString('base64'),
'Content-Type': 'application/json',
}
};
https.get(options, function (res) {
res.on('data', function (data) {
body += data;
});
res.on('end', function () {
var response = JSON.parse(body);
var i = response.total;
for (j = 0; j < i; j++) {
issueDesc.push(response.issues[j].key + ": " + response.issues[j].fields.summary + "\n");
}
issueDesc = issueDesc.join("");
resolve(issueDesc);
});
res.on('error', (err) => {
reject(err);
})
});
});
}
}
app.js
var getissue = require('./getresponse.js');
getissue.httpGet()
.then(response => {
console.log('your response', response);
}).catch(err => {
console.error('error', err);
});
You'll get the response in the 'then' block.

Categories