Google Cloud Functions req/res with Promise by node-fetch - javascript

I am a total newbie to Javascript and trying to set up a bridge between two of the services I use. The goal is simply take body or the request, do a promise api call to another service to respond with the body of that api call. I have been able to take the body of the request and send it to the service, but I'm having trouble receiving that response and making body of that response as a response of the function. Please help me out. Thank you.
var moment = require('moment');
var CryptoJS = require("crypto-js");
const fetch = require('node-fetch');
var unixtime = moment().unix();
var apiUser = process.env.apiUser;
var secret = process.env.apiKey;
var url = process.env.url;
exports.test = (req, res) => {
var message = req.body;
message = JSON.stringify(message);
var body = "{\n \"ops\": [{\n \"conv_id\": \"679690\",\n \"type\": \"create\",\n \"obj\": \"task\",\n \"data\": message\n }]\n}\n"
body = body.replace(/message/ig, message);
var signature = CryptoJS.enc.Hex.stringify(CryptoJS.SHA1(unixtime + secret + body + secret));
function request1() {
return new Promise((resolve, reject) => {
var options = fetch(url+apiUser+'/'+unixtime+'/'+signature, {
method: 'post',
body: body,
headers: { 'Content-Type': 'application/json' },
});
options.then(res => {
var result = res.json;
console.log(result);
resolve(result);
})
.catch(() => { // if .then fails
console.log('Promise rejected');
let rejectMessage = 'Sorry, an error occurred.';
reject(rejectMessage); // Promise rejected
});
});
}
request1();
};

you can retrieve result object easily because function request1 returns a promise resolving that object, so this should work:
request1().then((resultObject)=>{
//resultObject === result
return res.send(resultObject);
});
Also, res.json() returns a promise, so you should do:
options.then(res => res.json()).then(result => {
console.log(result);
resolve(result);
})

Related

What would be a proper way of returning this promise?

I'm new to coding and Promises are somewhat of an abstract concept for me. I know that in order to return a Promise you must use .then and .catch , but I'm unsure how exactly they should be used in the following code.
/**
* Make an HTTPS request to the MTA GTFS API for a given feed.
* #param {String} baseUrl - base URL for MTA GTFS API
* #param {String} feedId - identifier for which realtime feed
* #param {String} apiKey - key for MTA GTFS API
* #return {<Object>} - Promise of parsed feed.
*/
function makeRequest(baseUrl, feedId, apiKey) {
const feedUrl = baseUrl + feedId;
return new Promise((resolve, reject) => {
const req = https.request(feedUrl,
{ headers: { 'x-api-key': apiKey } },
(res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var data;
data = [];
res.on('data', (chunk) => {
return data.push(chunk);
});
return res.on('end', function() {
var msg;
data = Buffer.concat(data);
try {
msg = nstrDecoder.decode(data);
} catch (err) {
try {
console.log(err.message);
msg = nsssDecoder.decode(data);
} catch (err) {
console.log(err.message);
msg = "";
}
}
resolve(msg);
});
}
);
req.on('error', (e) => {
reject(e.message);
});
req.end();
});
}
return console.log(makeRequest(baseUrl, feedId, apiKey));
After running this code I get a message saying the Promise is pending. Thoughts??
When calling a function that returns a promise, you're going to get the promise in a pending state being it hasn't resolve or rejected yet. You have two options.
Option one.
node 10+ you can use async await.
async function main(){
const res = await makeRequest(baseUrl, feedId, apiKey)
return res
}
Pre node 10
makeRequest(baseUrl, feedId, apiKey)
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
Essentially if you have to wait for the data, you have to have the code that relies on that data after the await, or in the .then block.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Your makeRequest() function returns a promise. To use that promise, you use .then() or await to get the resolved value from the promise.
Using .then():
makeRequest(baseUrl, feedId, apiKey).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
or using await:
async function someFunction() {
try {
let result = await makeRequest(baseUrl, feedId, apiKey);
console.log(result);
} catch(e) {
console.log(err);
}
}
FYI, a lot of what makeRequest() is doing can be done simpler using an http request library that already supports promises. There is a list of competent libraries to choose from here.
My favorite is got(), but you can review the list to decide which one you like. The main advantages are that they already collect the whole response for you, already support promises, support a wide variety of authentication schemes with built-in logic and can often decode or parse the response for you - all things which https.request() does not know how to do by itself.

promise not working for me http.get

I am using a promise to get my response.
but he value is not coming in my console.
can you tell me whats the problem.
providing my code below
const https = require('https');
/*
* Complete the function below.
* Use console.log to print the result, you should not return from the function.
*/
function getMovieTitles(substr) {
return new Promise((resolve,reject) => {
https.get('https://jsonmock.hackerrank.com/api/movies/search/?Title=spiderman&page=1', (res) =>{
// var {statusCode} = res;
//var contentType = res.headers['content-type'];
console.log('res---->', res);
})
})
}
Here you go,
new Promise(function(resolve, reject) {
https.get('your_url_here', (res) =>{
// var {statusCode} = res;
// var contentType = res.headers['content-type'];
resolve(res);
})
}).then(function(res){
console.log("Response",res);
})
To Test Please Follow - https://jsfiddle.net/qdjkreo5/3741/
Alternatives :
You can deal with the data returned by https module in its callback itself as https also returns a callback.
const https = require('https');
https.get('https://encrypted.google.com/', (res) => {
console.log('response', res);
}).on('error', (e) => {
console.error(e);
});
Apart from that To Combine REST API calls with JavaScript Promises in node.js you can make use of the npm module request-promise.
var request = require('request-promise');
request({
"method":"GET",
"uri": "https://api.github.com/",
"json": true,
"headers": {
"User-Agent": "My little demo app"
}
}).then(console.log, console.log);
Building on the prior answers, here's a promise framework that handles both resolve (response, but not necessarily 200) and reject (no response). The axios package does this but hides the details. I like this a little better because it gives me more control over the logic.
const p1 = new Promise((resolve, reject) => {
https.get(url, (res) => {
let {statusCode} = res;
let contentType = res.headers['content-type'];
resolve(`status=${statusCode} type=${contentType}`); // response, so resolve
}).on('error', (err) => {
reject(err); // no response, so reject
});
});
p1.then(res => console.log(`${url}: ${res}`)) // resolve
.catch(err => console.log(err)) // reject
.finally(console.log('Finally')); // always
Test on the following URLs,
https://google.com
https://x.com
http://x.com

How to process the Promise returned from parent Promise chain with various possible outcomes

In my Node.JS web app, I have a database layer module (db_location.js). Depending the response type from the node-fetch call, it returns a json() or a text() promise depending on the content type from the response of the fetch call.
const querystring = require('querystring');
const fetch = require('node-fetch');
const p_conf = require('../parse_config');
const db_location = {
getLocations: function() {
return fetch(`${p_conf.SERVER_URL}/parse` + '/classes/GCUR_LOCATION', { method: 'GET', headers: {
'X-Parse-Application-Id': 'APPLICATION_ID',
'X-Parse-REST-API-Key': 'restAPIKey'
}}).then(function(res1) {
const contentType = res1.headers.get('content-type').toLowerCase();
if (contentType.includes("application/json"))
return res1.json(); // RETURNS A PROMISE
else
return res1.text(); // RETURNS A PROMISE
}).catch(err => Promise.reject(JSON.stringify({ "Error": err })));
}
};
module.exports = db_location
The module is called by a Express route locations.js.
const db_location = require('../db/db_location');
router.get('/', function(req, res, next) {
db_location.getLocations()
.then(function(result) {
/*
IF RESULT IS JSON, DO PROCESSING 1;
ELSE RESULT IS TEXT, DO PROCESSING 2.
*/
})
.catch((err) => {
return next(err);
})
});
As the response from the fetch cannot be passed down the promise chain, there is no way to retrieve the content type from in the route code.
Well Javascript strings are valid Json data. So in theory you can't really tell whether something was Json or not unless you transform it after res1.json()
You can check if something is of type string with the following in location.js
if(typeof result === 'string'){ /* it's a string*/ }
else{ /* it's something else */}

Async http-request, node.js modules and variables

I'm currently struggling to get variable values from one node.js module into another. This is my current problem:
I am fetching data from a REST API via https-request:
// customrequest.js
sendRequest( url, function( data, err ) {
if(err) {
console.log('--- Error ---');
console.log( err );
}
else {
console.log('--- Response ---');
console.log(data);
// output: data
return data;
}
module.exports = { sendRequest }
And my index.js file:
// index.js
let sendRequest = require('./customrequest');
let req;
req = sendRequest('google.com');
console.log(req);
// output: undefined
// how can I get the variable set, when request is getting data in response?
I totally understand, that the request to an API takes some time for the response. One solution is, that I just put everything into one js file. But as my project will get bigger over time, the modular approach is my goto-solution. Any suggestions on how to solve this?
Node uses callbacks for this situation. Try something like this:
// customrequest.js
sendRequest(url, callback)
module.exports = { sendRequest }
// index.js
let sendRequest = require('./customrequest');
let req = sendRequest('google.com', function (data, err) {
if (err) {
//handle error here
}
console.log(data);
};
// output: undefined
// how can I get the variable set, when request is getting data in response?
Thanks. The problem I encounter is somewhat different. I solved it with this code snippets … using async and await.
// request.js
const fetch = require('node-fetch')
async function myRequest (somestring) {
try {
let res = await fetch('https://api.domain.com/?endpoint='+somestring)
if (res.ok) {
if (res.ok) return res.json()
return new Error (res.status)
}
} catch (err) {
console.error('An error occurred', err)
}
}
module.exports = { myRequest }
// index.js
const request = require('./requests')
const myRequest = request.myRequest
let myVar;
myRequest('somestring')
.then(res => myVar = res.result)
setInterval(() => {
myRequest('somestring')
.then(res => myVar = res.result)
console.log(myVar)
}, 1000)
The async function and awaits return a promise. This promise is, when resolved, assigned to a variable.

Node.JS recursive promise not resolving

i'm working with an API that allows me to sync data to a local DB. There is a syncReady API that I'm calling recursively until the sync batch is ready to start sending data. The recursion is working correctly and the .then callback is called, but the resolve function never resolves the response.
const request = require('request-promise');
const config = require('../Configs/config.json');
function Sync(){}
Sync.prototype.syncReady = function (token, batchID) {
return new Promise((res, rej) => {
config.headers.Get.authorization = `bearer ${token}`;
config.properties.SyncPrep.id = batchID;
request({url: config.url.SyncReady, method: config.Method.Get, headers: config.headers.Get, qs: config.properties.SyncPrep})
.then((response) => {
console.log(`The Response: ${response}`);
res(response);
}, (error) => {
console.log(error.statusCode);
if(error.statusCode === 497){
this.syncReady(token, batchID);
} else rej(error);
}
);
});
};
I get the 497 logged and the "The Response: {"pagesTotal";0}" response but the res(response) never sends the response down the chain. I've added a console.log message along the entire chain and none of the .then functions back down the chain are firing.
I hope I've explained this well enough :-). Any ideas why the promise isn't resolving?
Thanks!
First, you don't need to wrap something that returns a promise with a new Promise. Second, for your error case you don't resolve the promise if it is 497.
const request = require('request-promise');
const config = require('../Configs/config.json');
function Sync(){}
Sync.prototype.syncReady = function (token, batchID) {
config.headers.Get.authorization = `bearer ${token}`;
config.properties.SyncPrep.id = batchID;
return request({url: config.url.SyncReady, method: config.Method.Get, headers: config.headers.Get, qs: config.properties.SyncPrep})
.then((response) => {
console.log(`The Response: ${response}`);
return response;
})
.catch((error) => {
console.log(error.statusCode);
if(error.statusCode === 497){
return this.syncReady(token, batchID);
} else {
throw error;
}
})
);
};
Maybe something like the above will work for you instead. Maybe try the above instead. As a general rule of thumb, it's you almost always want to return a Promise.

Categories