I keep getting "Unexpected end of JSON input" - node.js

app.get("/dashboard", function(req, res) {
const country = "Singapore";
// I used a constant in desperation
const url =
"https://api.covid19api.com/live/country/" + country;
https.get(url, function(response) {
response.on("data", function(data) {
const caseData = JSON.parse(data);
// Error stems from here
res.render("cases", { caseData: caseData });
});
});
});

You're not waiting for the entire message before trying to parse it.
The data event is invoked for each chunk of data from the HTTP response. The response may not necessarily fit in a single chunk.
You'll have to wait for the end event before trying to parse the JSON... or preferably use a higher-level library such as node-fetch for a more comfortable promise-based API.

Related

Node - Making a call to an ExpressJS route, and need to do a GET request to an API in it

I'm making a cryptocurrency dashboard for a project, and I'm completely new to Node and Express. This is what I have currently
app.get('/search', function(req,res){
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(req.url);
data = []
var options = {
"method": "GET",
"hostname": "rest.coinapi.io",
"path": "/v1/assets",
"headers": {'X-CoinAPI-Key': 'MY_API_KEY_HERE'}
};
const request = https.request(options, (data, response) => {
response.on('data', d => {
data.push(d);
})
});
console.log(data);
request.end();
res.end();
})
The idea is on my front end, I have a button that when clicked, will make a request to the CoinAPI api, getting all reported assets and current values. I'm not quite sure how I'm supposed to send that response data back to my frontend as a response. So, I tried to pull the response data out of the JSON that gets returned by the https.request line. I have a data array data = [] as you can see at the top of my code.
I originally had my request set up like:
const request = https.request(options, response => {
but when I would try to push d onto data, I console logged and the data array was empty. This makes sense, the data array is out of scope of the request function, so data doesn't get updated. But when I tried to pass data into the function, I errored out.
Basically I want to be able to send the JSON data back to my front end after making the request to CoinAPI. If I do process.stdout.write(d) in my https.request callback, I do actually see the coinapi data coming back. I just don't know how to send it to the front end as part of my response.
Issues:
The use of (data, response) is incorrect. The first and only argument is response so it should be (response).
The on('data') event receives buffered data. Concatenating it to a final string is the standard usage, not appending an array.
You're missing an on('end') which you should use to output the final data or do any processing.
Using res.write you're sending a text/html content type and some content which you don't want if the goal is to output JSON which the frontend can parse and use.
Missing an error handler for the API call.
Complete updated code:
app.get('/search', function(req,res){
let str = '';
const options = {
"method": "GET",
"hostname": "rest.coinapi.io",
"path": "/v1/assets",
"headers": {'X-CoinAPI-Key': 'MY_API_KEY_HERE'}
};
const request = https.request(options, (response) => {
response.on('data', d => {
str += d;
});
response.on('end', () => {
try {
let obj = JSON.parse(str);
// do any manipulation here
res.json(obj);
} catch(e){
console.log(e);
res.status(500).json({ message: 'Something went wrong - parse error' });
}
});
});
request.end();
request.on('error', (e) => {
console.log(e);
res.status(500).json({ message: 'Something went wrong - req error' });
});
});
I added a JSON.parse() to show how you'd handle that if you wanted to do some manipulation of the data before sending it to the frontend. If you simply want to return the exact response of the coin API then use an end event like:
response.on('end', () => {
res.json(str);
});
To send JSON data back to the client as response all you need to do is :
return res.json(data);
Its as simple as this. :)

Incomplete JSON response with Node.js https-module

Calling the Riot-Api Im receiving incomplete JSON on a https GET-request.
After debugging, I realized that depending how much I wait (breakpoint) pre-executing the
https on'data' callback Im actually receiving the complete JSON object.
(Average API response time for me is 200-300ms)
let getOptions = function(url) {
return {
host: 'na.api.pvp.net',
port: 443,
path: `${url}?api_key=${apiKey}`,
method: 'GET'
};
}
exports.Call = function(url, callback) {
let response = {};
let req = https.request(getOptions(url), function(res) {
response.statusCode = res.statusCode;
res.on('data', function(data) {
response.json = JSON.parse(data);
callback(response);
});
});
req.on('error', function(err) {
response.err = err;
callback(response);
});
req.end();
};
Running the code without breakpoints or only breaking a short time I run either into error:
JSON.parse(data): Unexpected Token in JSON at position ...
or
JSON.parse(data): Unexptected end of JSON Input.
As I expect the 'data' callback to be executed only after the request is complete im confused about how to fix it (without artificially delaying it ofc.).
http.request returns a stream – its not a simple callback that contains the whole response.
You will have to buffer and concatenate everything if you want to parse the whole response.
I would strongly recomment to use a helper library like got or request

howto extract data from upcdatabase request

in my project I have to do a request to upcDatabase.com, I amworking with nodeJS, I get the answer from the server but I do not how to extractthe data this are the important part of my code:
module.exports = function (http,upc){
var upc_ApiKey = "XXX",
url = "http://upcdatabase.org/api/json/"+upc_ApiKey+'/'+upc;
http.get(url,function(resp){
// my code to read the response
I do not get any error, but the resp is a big Json and I do not know where to find the data
I would recommend you using the superagent module. It provides much more functionality than the built-in http request and it will automatically parse the response for you.
request
.get(url)
.end(function(err, res) {
if (res.ok) {
// Her ethe res object will be already parsed. For example if
// the server returns Content-Type: application/json
// res will be a javascript object that you can query for the properties
console.log(res);
} else {
// oops, some error occurred with the request
// you can check the err parameter or the res.text
}
});
You could achieve the same with the built-in http module but with much more code:
var opts = url.parse(url);
opts.method = "GET";
var req = http.request(opts, function (res) {
var result = "";
res.setEncoding("utf8");
res.on("data", function (data) {
result += data;
});
if (res.statusCode === 200) {
res.on("end", function () {
// Here you could use the result object
// If it is a JSON object you might need to JSON.parse the string
// in order to get an easy to use js object
});
} else {
// The server didn't return 200 status code
}
});
req.on("error", function (err) {
// Some serious error occurred during the request
});
// This will send the actual request
req.end();

Node.js and the connect module: How do I get the message out of a request object sent to a web server

I'm using Node.js and connect to create a simple web server. I have something similar to the following code and I can't figure out how to access the actual request message body from the request object. I'm new to this so bear with me. I'm also taking out some of the stuff that's not necessary for the example.
function startServer(dir) {
var port = 8888,
svr = connect().use(connect.static(dir, {"maxAge" : 86400000}))
.use(connect.directory(dir))
/*
* Here, I call a custom function for when
* connect.static can't find the file.
*/
.use(custom);
http.createServer(svr).listen(port);
}
function custom(req, res) {
var message = /* the message body in the req object */;
// Do some stuff with message...
}
startServer('dirName');
Make sense? I've tried logging that object to the console and it is full of TONS of stuff. I can easily see headers in there plus the request URL and method. I just can't seem to isolate the actual message body.
You should include the connect.bodyParser middleware as well:
svr = connect().use(connect.static(dir, {"maxAge" : 86400000}))
.use(connect.directory(dir))
.use(connect.bodyParser())
.use(custom);
That will provide the parsed message body as req.body to your handler.
If you want the raw message body, you shouldn't use it but instead read the req stream yourself:
function custom(req, res) {
var chunks = [];
req.on('data', function(chunk) {
chunks.push(chunk);
});
req.on('end', function() {
var rawbody = Buffer.concat(chunks);
...do stuff...
// end the request properly
res.end();
});
}
if(req.method == "POST"){
var body = '';
req.on('data', function(data){
body += data;
});
}
Then body should contain your message if you posted correctly.
A better idea would be to use Express, then use the bodyparser middleware - which will give you this functionality out of the box without worrying about somebody hammering your server. The code above has NO functionality to worry about attacks - but it will get you started.

How to make web service calls in Expressjs?

app.get('/', function(req, res){
var options = {
host: 'www.google.com'
};
http.get(options, function(http_res) {
http_res.on('data', function (chunk) {
res.send('BODY: ' + chunk);
});
res.end("");
});
});
I am trying to download google.com homepage, and reprint it, but I get an "Can't use mutable header APIs after sent." error
Anyone know why? or how to make http call?
Check out the example here on the node.js doc.
The method http.get is a convenience method, it handles a lot of basic stuff for a GET request, which usually has no body to it. Below is a sample of how to make a simple HTTP GET request.
var http = require("http");
var options = {
host: 'www.google.com'
};
http.get(options, function (http_res) {
// initialize the container for our data
var data = "";
// this event fires many times, each time collecting another piece of the response
http_res.on("data", function (chunk) {
// append this chunk to our growing `data` var
data += chunk;
});
// this event fires *one* time, after all the `data` events/chunks have been gathered
http_res.on("end", function () {
// you can use res.send instead of console.log to output via express
console.log(data);
});
});

Resources