Undefined in console on GET request in NodeJS - node.js

I'm creating a script which is going to automatically receive data from API and store it in MongoDB at specific UTC time.
I use Node-Schedule for scheduling a task at specific time.
CoinMarketCap API for receiving a real time data.
Problem: I receive an undefined in console every second(since the node-schedule is configured to call the code every second). I was looking for any syntax errors or errors in API and wasn't successful. I understand that I receive undefined cause the function doesn't return anything.
Currently doesn't have any ideas what wrong with it at all.
All API keys and DB username password was correct I checked it as well.
Goal: To have the script which is automatically receives data from API and stores it MongoDB collection.
Full Code
const { MongoClient } = require('mongodb');
const schedule = require('node-schedule');
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
const saveToDatabase = function(BTCdata) {
const url = 'mongodb+srv://name:password#cluster0-1kunr.mongodb.net/<dbname>?retryWrites=true&w=majority';
MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, db) => {
if (err) throw err;
const dbo = db.db('Crypto');
const myobj = { Name: 'BTC', Volume: 'BTCdata' };
dbo.collection('Crypto-Values').insertOne(myobj, (error, res) => {
if (error) throw error;
console.log('1 document inserted');
db.close();
});
});
};
function request(method, url) {
return new Promise(((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = resolve;
xhr.onerror = reject;
xhr.send();
}));
}
const j = schedule.scheduleJob('* * * * * *', () => {
request(
'GET',
'http://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=API-KEY-HERE',
)
.then((r1) => {
const x1 = JSON.parse(r1.target.responseText);
const BTCdata = x1.data.find((d) => d.symbol === 'BTC').quote.USD.volume_24h; // creating a variable to store a BTC request from API
console.log(BTCdata);
// Saving to database
saveToDatabase(BTCdata);
})
.catch((err) => {
console.log(err);
});
});
EDIT1:
This is a console log of x1 value.
EDIT2:
Was missing this part -
var request = require('request');
After it was added I start receiving a new error in my console which is :
events.js:287
throw er; // Unhandled 'error' event
^
Error: Invalid URI "GET"
at Request.init
at new Request
at request
at Job.job
at Job.Invoke
at Users/path to node modules/node-schedule
at Timeout.onTimeout
EDIT3:
After correction to the code with #Sureshprajapati answer.
New error appears - TypeError: Cannot read property 'responseText' of undefined
Trying to find solution by myself. Still looking for any advice. Thank you.
var requestPromise = require('request-promise');
requestPromise.get({
uri: 'http://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=API-KEY-HERE',
json: true
}).then(r1 => {
const x1 = JSON.parse(r1.target.responseText);
const BTCdata = x1.data.find(d => d.symbol === 'BTC').quote.USD
.volume_24h; // creating a variable to store a BTC request from API
console.log(BTCdata);
// Saving to database
saveToDatabase(BTCdata);
}).catch(err => {
console.log(err);
});

request supports both streaming and callback interfaces natively. If you'd like request to return a Promise instead, you can use an alternative interface wrapper for request. These wrappers can be useful if you prefer to work with Promises, or if you'd like to use async/await in ES2017.
request-promise (uses Bluebird Promises)
This module is installed via npm:
npm install --save request
npm install --save request-promise
Coming to modifying your code as per documentation:
var requestPromise = require('request-promise');
requestPromise.get({
uri: 'http://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?CMC_PRO_API_KEY=API-KEY-HERE',
json: true
}).then(x1 => {
const BTCdata = x1.data.find(d => d.symbol === 'BTC').quote.USD
.volume_24h; // creating a variable to store a BTC request from API
console.log(BTCdata);
// Saving to database
saveToDatabase(BTCdata);
}).catch(err => {
console.log(err);
});

Related

Calling a firebase cloud function (with POST)

Here is the code for a working firebase cloud function and following is a question about some change I want to make and can't at this point, even after trying several options.
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
import * as cors from "cors";
const corsHandler = cors({origin: true});
admin.initializeApp(functions.config().firebase);
exports.myFunc = functions.https.onRequest(function(req, resp) {
corsHandler(req, resp, async () => {
const from = String(req.query.from); // Through the URL.
// const from = req.body.from; // I tried this among other things to get the information through a POST but it did not work.
admin.auth().getUserByEmail(from)
.then(function(userRecord) {
console.log("Successfully fetched user data:",
userRecord.toJSON());
})
.catch(function(error) {
console.log("Error fetching user data:", error);
});
});
});
This function can be called using a URL like:
https://us-central1-myapp.cloudfunctions.net/myFunc?from=example#example.com
But what I want is to be able to call it through a POST from a JS page (with XMLHttpRequest) in my web app.
How should I change the function above for that?
For reference, this is the kind of code I am trying to use on the web-app side to call the cloud function (but it is not working):
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://us-central1-myapp.cloudfunctions.net/myFunc", true, 'me#example.com', 'VerySecretPassWord');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send({"from": "example#example.com"});
xhr.onload = function() {
var data = JSON.parse(this.responseText);
console.log('THE DATA:',data);
};

Firebase CORS - Unhandled error typeError: Cannot read property origin of undefined

Currently trying to fun a google cloud function directly from my webapp which makes a mongodb query and returns the data to the clients browser. And I get the following error.
i functions: Beginning execution of "us-central1-fullName"
> {"verifications":{"app":"MISSING","auth":"MISSING"},"logging.googleapis.com/labels":{"firebase-log-type":"callable-request-verification"},"severity":"INFO","message":"Callable request verification passed"}
> {"severity":"ERROR","message":"Unhandled error TypeError: Cannot read property 'origin' of undefined\n at C:\\Users\\myalt\\s\\functions\\node_modules\\cors\\lib\\index.js:219:40\n at optionsCallback (C:\\Users\\myalt\\s\\functions\\node_modules\\cors\\lib\\index.js:199:9)\n at corsMiddleware (C:\\Users\\myalt\\s\\functions\\node_modules\\cors\\lib\\index.js:204:7)\n at C:\\Users\\myalt\\s\\functions\\index.js:6:5\n at newHandler (C:\\Users\\myalt\\AppData\\Roaming\\npm\\node_modules\\firebase-tools\\lib\\emulator\\functionsEmulatorRuntime.js:296:16)\n at C:\\Users\\myalt\\s\\functions\\node_modules\\firebase-functions\\lib\\common\\providers\\https.js:322:32\n at processTicksAndRejections (internal/process/task_queues.js:93:5)"}
i functions: Finished "us-central1-fullName" in ~1s
I was originally receiving this error:
Access to fetch at 'https://us-central1--8955f.cloudfunctions.net/fullName' from origin 'https://-8955f.web.app' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
However, I fixed this by adding
const cors = require('cors')({ Origin: true });
exports.fullName = functions.region('us-central1').https.onCall((req, res) => {
cors(req, res, () => {
// my function
}
I'm not entirely sure why this origin error is happening. Any help would be very appreciated.
const cors = require('cors')({ Origin: true });
exports.fullName = functions.region('us-central1').https.onCall((req, res) => {
cors(req, res, () => {
return new Promise((resolve, reject) => { // 👈 return a promise, so Cloud Functions knows to wait
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://:/?readPreference=primary&appname=MongoDB%20Compass&ssl=false";
MongoClient.connect(url, function (err, db) {
console.log('Connected to database.')
if (err) throw err;
var dbo = db.db("discord_pandemic");
dbo.collection("ArrayStats").find().toArray(function (err, result) {
if (err) throw err;
const totalInfections = result[0].total_infections
const totalUsers = result[0].total_users
const totalCommands = result[0].total_commands
const totalLiveInfections = result[0].total_live_infections
const totalLiveInfectedChannels = result[0].total_live_infected_channels
const data = {
totalInfections: totalInfections,
totalUsers: totalUsers,
totalCommands: totalCommands,
totalLiveInfections: totalLiveInfections,
totalLiveInfectedChannels: totalLiveInfectedChannels
}
resolve(data); // 👈 resolve the promise with the data for the user
});
});
});
})
})
The error messages: “TypeError: Cannot read property 'origin' of undefined” indicates that within your code you are calling the property: code/status, while this property is being NULL. This could be a callback confusion such as here where it is mentioned that is because send is probably not returning a promise, it uses the callback method instead. On the other hand, this could be just missing a definition just like here or here. Not sure if you have checked the shared links before, but if not, I would recommend you to check them.

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client, on second button press

I'm currently learning Flutter/DART and I'm trying to build app that communicates with the server but I have problems. There is a Sign-In button that sends request to the server, server checks if there is user with that username and password and response gets sent back. Everything is working like charm for the first button press, but if button is pressed for the second time and so on error pop ups on server:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:526:11)
at ServerResponse.header (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\node_modules\express\lib\response.js:267:15)
at ServerResponse.send (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\node_modules\express\lib\response.js:158:21)
at Function.<anonymous> (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\server.js:14:39)
at Function.emit (events.js:326:22)
at Query.<anonymous> (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\db.js:17:20)
at Query.<anonymous> (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\node_modules\mysql\lib\Connection.js:526:10)
at Query._callback (C:\Users\PC\Desktop\Flutter Node.js Login-Register App\server\node_modules\mysql\lib\Connection.js:488:16) {
code: 'ERR_HTTP_HEADERS_SENT'
}
Here is the code from flutter app and from node.js server:
Flutter:
Function gets called on button press:
void signIn(username, password) async {
final bodyEncoded = jsonEncode(
{
'username': model.username,
'password': model.password,
},
);
final response = await http.post(
'http://192.168.0.110:3000/signin',
headers: {'Content-Type': 'application/json'},
body: bodyEncoded,
);
if (response.body == 'true')
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MainBody(),
),
);
else
setState(() => {elementOpacity = 1.0});
}
Server
server.js
const express = require('express');
const app = express();
const server = require('./server.json');
exports.app = app;
const db = require('./db');
app.use(express.json());
app.post('/signin', (req, res) => {
const {username, password} = req.body;
app.emit('signInRequest', username, password);
app.on('response', (value) => res.send(value));
});
app.listen(server.port, () => console.log('Server running on port ' + server.port));
db.js
const mysql = require('mysql');
const connectionUri = require('./db.json');
const db = mysql.createConnection(connectionUri);
const app = require('./server').app;
db.connect((err) => {
if (err)
throw err;
console.log('MySQL connection successful!');
});
app.on('signInRequest', (username, password) => {
db.query("SELECT Count(*) AS 'count' FROM Users WHERE (Username = '" + username + "' AND Password = '" + password + "')", (err, result) => {
if(err)
throw err;
return app.emit('response', result[0].count == 1 ? true : false);
});
});
I will appreciate any help!
When you rely on events, but you don't tie those events to specific request, you're going to have a bad time: right now, requests map to "nothing", but then the code goes on to tie the event that triggers a response to every app.on("response", ...) binding that you ever declared... and in the process, also preventing responses from ever getting garbage collected because they keep needing to stick around for event handling. So your code also has what is effectively a memory leak.
Instead of doing this with events, use promises/async functions and then await their return value. That way one request maps to one response, and things get cleaned up properly too.
server.js
const express = require('express');
const app = express();
const database = require('./database.js');
const server = require('./server.json');
exports.app = app;
app.use(express.json());
app.post('/signin', async (req, res, next) => {
const { username, password } = req.body;
if (!validateUsernameAndPassword(username, password)) {
// Remember to write a global error handler. Or don't use
// Express' next(err) functionality but respond with a call-appropriate
// response that has the correct HTTP error code, too.
next(new Error("you better believe this should be an error"));
}
const value = await database.handleSignInRequest(username, password);
res.send(value);
});
app.listen(server.port, () => console.log('Server running on port ' + server.port));
database.js:
const mysql = require('mysql');
const connectionUri = require('./db.json');
const db = mysql.createConnection(connectionUri);
// the db code shouldn't have to know anything about express or express apps
db.connect((err) => {
if (err)
throw err;
console.log('MySQL connection successful!');
});
function handleSignInRequest(username, password) {
return new Promise((resolve, reject) => {
// Now, a super important note: NEVER QUERY A DATABASE DIRECTLY LIKE THIS:
db.query(`SELECT Count(*) AS 'count' FROM Users WHERE (Username = '${username}' AND Password = '${password}')`, (err, result) => {
if(err) return reject(err);
resolve(result[0].count == 1 ? true : false);
});
// Look up how to query your database in a sanitized, prepared statement
// fashion. https://xkcd.com/327/ is a word famous piece of satire for
// good reason.
//
// Please read https://www.npmjs.com/package/mysql#preparing-queries and
// put that into practice.
});
});
module.exports = { handleSignInRequest };

ECONNRESET error while fetching an URL in node.js

Getting the below error when trying to fetch (using npm node-fetch) html from the link below:
Failed to fetch page: { FetchError: request to
https://www1.nseindia.com/marketinfo/companyTracker/compInfo.jsp?symbol=TCS&series=EQ
failed, reason: read ECONNRESET
at ClientRequest
I am using the following snippet :
const DomParser = require("dom-parser");
const parser = new DomParser();
const fetch = require("node-fetch");
router.get("/info", (req, res, next) => {
var url =
"https://www1.nseindia.com/marketinfo/companyTracker/compInfo.jsp?symbol=TCS&series=EQ";
fetch(url)
.then(function(response) {
// When the page is loaded convert it to text
return response.text();
})
.then(function(html) {
// Initialize the DOM parser
// Parse the text
var doc = parser.parseFromString(html, "text/html");
// You can now even select part of that html as you would in the regular DOM
// Example:
// var docArticle = doc.querySelector('article').innerHTML;
console.log(doc);
})
.catch(function(err) {
console.log("Failed to fetch page: ", err);
});
});
The response was consoled log few times before showing the error and now its throwing err everytime I call /info.
I have tried the snippet in repl online editor. it returns Promise {pending}.
I would use some modern promise based packages to do this job. some are got, axios etc. node-fetch was last published 8 months ago. It might not be able to handle the encoding or compressing.
here is an example using axios which works.
const axios = require("axios");
const DomParser = require("dom-parser");
const parser = new DomParser();
var url =
"https://www1.nseindia.com/marketinfo/companyTracker/compInfo.jsp?symbol=TCS&series=EQ";
axios(url)
.then(response => response.data)
.then(html => {
// Initialize the DOM parser
// Parse the text
var doc = parser.parseFromString(html, "text/html");
// You can now even select part of that html as you would in the regular DOM
// Example:
// var docArticle = doc.querySelector('article').innerHTML;
console.log(doc);
})
.catch(function(err) {
console.log("Failed to fetch page: ", err);
});
As far as I know you cannot use fetch in NodeJS. You need to rely in different methods. I personally usually use Axios.
Check also if this part of the code actually returns a promise:
return response.text()
Before chaining another .then.
https://www.valentinog.com/blog/http-js/

How do I call a third party Rest API from Firebase function for Actions on Google

I am trying to call a rest API from Firebase function which servers as a fulfillment for Actions on Google.
I tried the following approach:
const { dialogflow } = require('actions-on-google');
const functions = require('firebase-functions');
const http = require('https');
const host = 'wwws.example.com';
const app = dialogflow({debug: true});
app.intent('my_intent_1', (conv, {param1}) => {
// Call the rate API
callApi(param1).then((output) => {
console.log(output);
conv.close(`I found ${output.length} items!`);
}).catch(() => {
conv.close('Error occurred while trying to get vehicles. Please try again later.');
});
});
function callApi (param1) {
return new Promise((resolve, reject) => {
// Create the path for the HTTP request to get the vehicle
let path = '/api/' + encodeURIComponent(param1);
console.log('API Request: ' + host + path);
// Make the HTTP request to get the vehicle
http.get({host: host, path: path}, (res) => {
let body = ''; // var to store the response chunks
res.on('data', (d) => { body += d; }); // store each response chunk
res.on('end', () => {
// After all the data has been received parse the JSON for desired data
let response = JSON.parse(body);
let output = {};
//copy required response attributes to output here
console.log(response.length.toString());
resolve(output);
});
res.on('error', (error) => {
console.log(`Error calling the API: ${error}`)
reject();
});
}); //http.get
}); //promise
}
exports.myFunction = functions.https.onRequest(app);
This is almost working. API is called and I get the data back. The problem is that without async/await, the function does not wait for the "callApi" to complete, and I get an error from Actions on Google that there was no response. After the error, I can see the console.log outputs in the Firebase log, so everything is working, it is just out of sync.
I tried using async/await but got an error which I think is because Firebase uses old version of node.js which does not support async.
How can I get around this?
Your function callApi returns a promise, but you don't return a promise in your intent handler. You should make sure you add the return so that the handler knows to wait for the response.
app.intent('my_intent_1', (conv, {param1}) => {
// Call the rate API
return callApi(param1).then((output) => {
console.log(output);
conv.close(`I found ${output.length} items!`);
}).catch(() => {
conv.close('Error occurred while trying to get vehicles. Please try again later.');
});
});

Resources