async / await for Node.js https.get - node.js

I'm trying to simplify code with async / await
But have problems making https.get with async / await structure.
I am aware of how to do this with third-party modules but prefer the native node.js https module.
Below code doesn't work for me:
async function get_page() {
const https = require('https')
const url = 'https://example.com'
const util = require('util')
const https_get = util.promisify(https.get)
const data = await https_get(url)
do_awesome_things_with_data(data)
}
This code working fine:
function get_page() {
const https = require('https')
const url = 'https://example.com'
let data = ''
https.get(url, res => {
res.on('data', chunk => { data += chunk })
res.on('end', () => {
do_awesome_things_with_data(data)
})
})
}

https.get doesn't return something that can be promisified as the signature of the callback doesn't match (err, value), so you can't await it.
However, you can wrap the https.get call within a Promise, like so, then await when calling get_page
const https = require('https')
async function get_page() {
const url = 'https://example.com'
return new Promise((resolve) => {
let data = ''
https.get(url, res => {
res.on('data', chunk => { data += chunk })
res.on('end', () => {
resolve(do_awesome_things_with_data(data));
})
})
})
}
// usage
(async () => await get_page())()
Edits
I've updated my answer to include the note of https.get not being able to be promisified and moved the require('https') outside of the function call.

Instead of promisify, roll your own function, or use a 3rd party library. Promisify cannot wrap what https.get returns.
// generic promise method for https
const requestPromise = ((urlOptions, data) => {
return new Promise((resolve, reject) => {
const req = https.request(urlOptions,
(res) => {
let body = '';
res.on('data', (chunk) => (body += chunk.toString()));
res.on('error', reject);
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode <= 299) {
resolve({statusCode: res.statusCode, headers: res.headers, body: body});
} else {
reject('Request failed. status: ' + res.statusCode + ', body: ' + body);
}
});
});
req.on('error', reject);
req.write(data, 'binary');
req.end();
});
});
Then call it like this:
async function get_page() {
const url = 'https://example.com'
const data = await requestPromise({url, method:'GET'})
do_awesome_things_with_data(data)
}
Or simply use a library such as axios to return a native promise, and also handle additional boilerplate cases.

Here is how I did it:
async myFunc = function {
let url = 'http://your.data/file';
let promise = new Promise((resolve, reject) => {
var data = '';
https.get(url, res => {
res.on('data', chunk => { data += chunk })
res.on('end', () => {
resolve(data);
})
})
});
let result = await promise; // wait until the promise resolves
doStuffWithResult(result);
};
In my case, I was fetching a .json file, so I actually used resolve(JSON.parse(data)) to return cleanely JSON object.

Related

Getting api using https request function

Well I had a bit problems
function getId(username){
const https = require("https")
let id = ""
let data = ``
https.get(`https://api.roblox.com/users/get-by-username?username=${username}`, (response) =>{
response.on('data', (chunk) => {
data += chunk;
})
response.on('end', () =>{
if(data){
id = JSON.parse(data).Id
}
})
})
.on('error', (error) => {
console.log(error)
})
return id
}
So my goal here to use getId("IHZAQSTORM33") and expected result, it will return user id (1684676332). but instead, it give me (""). It give me a colons
yes I'm trying to connect to roblox api.
Use promise to return the response object as shown in the code.
You can also refer to link for the comment by nkron:
Where is body in a nodejs http.get response?
function getId(username) {
const https = require('https');
let id = '';
let data = '';
const url = `https://api.roblox.com/users/get-by-username?username=${username}`;
return new Promise((resolve, reject) => {
https
.get(url, (response) => {
response.on('data', (chunk) => {
data += chunk;
});
response.on('end', () => {
resolve(data);
});
})
.on('error', reject);
});
//return id
};
(async () => {
const responseObject = await getId('IHZAQSTORM33');
})();
For more on async and await, refer this link:
await is only valid in async function

In NodeJS, how do I await the response of the http2 client library GET call?

I'm using the http2 client package with nodeJS. I want to execute a simple get request, and await the response from the server. So far I have
import * as http2 from "http2";
...
const clientSession = http2.connect(rootDomain);
...
const req = clientSession.request({ ':method': 'GET', ':path': path });
let data = '';
req.on('response', (responseHeaders) => {
// do something with the headers
});
req.on('data', (chunk) => {
data += chunk;
console.log("chunk:" + chunk);
});
req.on('end', () => {
console.log("data:" + data);
console.log("end!");
clientSession.destroy();
});
process.exit(0);
But what I"m not able to figure out is how to do I await the response of the request before exiting? Right now the code flies through to the process.exit line and I can't see a way to block until the request is done.
If you want to await it, then you have to encapsulate it into a function that returns a promise and you can then use await on that promise. Here's one way to do that:
import * as http2 from "http2";
...
function getData(path) {
return new Promise((resolve, reject) => {
const clientSession = http2.connect(rootDomain);
const req = clientSession.request({ ':method': 'GET', ':path': path });
let data = '';
req.on('response', (responseHeaders) => {
// do something with the headers
});
req.on('data', (chunk) => {
data += chunk;
console.log("chunk:" + chunk);
});
req.on('end', () => {
console.log("data:" + data);
console.log("end!");
clientSession.destroy();
resolve(data);
});
req.on('error', (err) => {
clientSession.destroy();
reject(err);
});
});
}
async function run() {
let data = await getData(path);
// do something with data here
}
run().then(() => {
process.exit(0);
}).catch(err => {
console.log(err);
process.exit(1);
});
The other way to do this is to use a higher level http library that does much of this work for you. Here's an example using the got module:
import got from 'got';
async function run() {
let data = await got(url, {http2: true});
// do something with data here
}
In this case, the got() module already supports http2 for you (if you specify that option), already collects the whole response for you and already supports promises (all the things your code needed to add in your original version).
Note, the GET method is the default method which is why it is not necessary to specify it here.
response = await new Promise(async (resolve, reject)=> {
let data = '';
req.on('data', async (chunk) => {
data += chunk;
resolve(JSON.parse(data));
});
});

How to make https.get request assign data from within the request to a variable outside of the request?

I'm trying to get an https.get request to assign data from within the request to a variable outside of the request. I'm also using axios. Within the https.get request, it returns the data I want in the res.on('end'... But I can't figure out how to get that data outside of the res.on('end'... portion of the request. Here is my code:
require('dotenv').config();
const express = require('express');
const {SERVER_PORT} = process.env;
const https = require('https');
const xml2js = require('xml2js');
const parser = new xml2js.Parser({ attrkey: "ATTR" });
const app = express();
app.use(express.json());
app.post('/api/ecb/forex/stats', async(req, res) => {
const {base_currency, base_amount, target_currency} = req.body;
let currencyInfo = https.get("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml", function(res) {
let data = '';
res.on('data', async function(stream) {
data += stream;
});
res.on('end', async function(){
parser.parseString(data, async function(error, result) {
if(error === null) {
return result['gesmes:Envelope'].Cube[0].Cube.forEach(element => {
console.log("at",element.Cube);
return element.Cube;
});;
}
else {
console.log(error);
}
});
});
});
console.log(currencyInfo);
})
const port = SERVER_PORT;
app.listen(port, () => console.log(`Port running on port ${port}`));
I want the value of 'element.Cube;' within the res.on('end"... portion of the https.get request to be assigned to the variable "currencyInfo". What am I doing wrong and how do I fix the code?
You can change your code to something like below, then you have Promise to return:
let currencyInfo = await new Promise((resolve, reject) => {
https.get('https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml', function(res) {
let data = '';
res.on('data', async function(stream) {
data += stream;
});
return res.on('end', async function() {
return parser.parseString(data, async function(error, result) {
if(error === null) {
return result['gesmes:Envelope'].Cube[0].Cube.forEach(element => {
resolve(element.Cube);
});
}
else {
reject(error);
}
});
});
});
});

Fetch and post text in NodeJS

I'm trying to grab text from an API that only returns a string of text ((here)) and having troubles throwing that out in a response. When posting, it comes out as [object Response], and the console.log doesn't show the text I want out of it.
The code I'm using:
fetch('http://taskinoz.com/gdq/api').then(
function(response) {
console.log(response);
throttledSendMessage(channel, response);
return response;
})
.catch(function(error) {
throttledSendMessage(channel, "An error has occured");
})
Log can be found here
Thanks for looking with me, couldn't find a solution :/
I think that because fetch returns a Response you need to call one of the functions on Response in order to get at the body's text. Here's an example:
fetch('https://github.com/')
.then(res => res.text())
.then(body => console.log(body));
Probably the problem is in async behavior of node.js. You can read more here
Also, I'm assume you use this package to make fetch request in node.js.
And assume that throttledSendMessage function is synchronous.
About your problem, just try to rewrite co de to use async/await for cleaner solution.
// We'll use IIFE function
(async () => {
const fetchResult = await fetch('http://taskinoz.com/gdq/api')
// Here fetch return the promise, so we need to await it again and parse according to our needs. So, the result code would be this
const data = await fetchResult.text();
throttledSendMessage(channel, data);
return data;
})()
Fetch is not available in nodejs , you could use the node-fetch https://www.npmjs.com/package/node-fetch , or you could use this fetch function:
const https = require('https');
const http = require('http');
function fetch(url, options = {}) {
return new Promise((resolve, reject) => {
if (!url) return reject(new Error('Url is required'));
const { body, method = 'GET', ...restOptions } = options;
const client = url.startsWith('https') ? https : http;
const request = client.request(url, { method, ...restOptions }, (res) => {
let chunks = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
chunks += chunk;
});
res.on('end', () => {
resolve({ statusCode: res.statusCode, body: chunks });
});
});
request.on('error', (err) => {
reject(err);
});
if (body) {
request.setHeader('Content-Length', body.length);
request.write(body);
}
request.end();
});
}
module.exports = fetch;
you could use it like this inside your code :
const result = await fetch(
`https://YOUR_URL`,
{
method: 'PUT',
body: JSON.stringify({ test: 'This is just a test', prio: 1 }),
},
);

Reading content from URL with Node.js

I'm trying to read the content from a URL with Node.js but all I seem to get are a bunch of bytes. I'm obviously doing something wrong but I'm not sure what. This is the code I currently have:
var http = require('http');
var client = http.createClient(80, "google.com");
request = client.request();
request.on('response', function( res ) {
res.on('data', function( data ) {
console.log( data );
} );
} );
request.end();
Any insight would be greatly appreciated.
try using the on error event of the client to find the issue.
var http = require('http');
var options = {
host: 'google.com',
path: '/'
}
var request = http.request(options, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
console.log(data);
});
});
request.on('error', function (e) {
console.log(e.message);
});
request.end();
HTTP and HTTPS:
const getScript = (url) => {
return new Promise((resolve, reject) => {
const http = require('http'),
https = require('https');
let client = http;
if (url.toString().indexOf("https") === 0) {
client = https;
}
client.get(url, (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
resolve(data);
});
}).on("error", (err) => {
reject(err);
});
});
};
(async (url) => {
console.log(await getScript(url));
})('https://sidanmor.com/');
the data object is a buffer of bytes. Simply call .toString() to get human-readable code:
console.log( data.toString() );
reference: Node.js buffers
A slightly modified version of #sidanmor 's code. The main point is, not every webpage is purely ASCII, user should be able to handle the decoding manually (even encode into base64)
function httpGet(url) {
return new Promise((resolve, reject) => {
const http = require('http'),
https = require('https');
let client = http;
if (url.toString().indexOf("https") === 0) {
client = https;
}
client.get(url, (resp) => {
let chunks = [];
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
chunks.push(chunk);
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
resolve(Buffer.concat(chunks));
});
}).on("error", (err) => {
reject(err);
});
});
}
(async(url) => {
var buf = await httpGet(url);
console.log(buf.toString('utf-8'));
})('https://httpbin.org/headers');

Resources