I hope you could please help me out, I'm running Node.js and trying to get a the city name from a API and it keeps showing an error
saying it Cannot read property city_name of undefined.
It gets stuck on this line in the code:
const cityName = weatherData.data.city_name;
Any clue why its doing that? Please
// Creating the server of the weather app
const express = require('express');
const app = express();
const { StringDecoder } = require('string_decoder');
const decoder = new StringDecoder('utf8');
const https = require('https');
app.get('/', (req, res) => {
const weatherPath = "https://api.weatherbit.io/v2.0/current?key=41c0f84d717a4764a26d144aa33a9443&city=melbourne,Australia"
// Calling the weather app
https.get(weatherPath, (response) => {
console.log(response.statusCode);
// Getting the data from the weather app
response.on('data', (d) => {
//console.log(d);
// Converting the buffer data from the weather app
console.log(decoder.write(d));
const weatherData = decoder.write(d);
const cityName = weatherData.data.city_name;
console.log(cityName);
});
});
res.send("The server is up and running on the web");
});
app.listen(3000, () =>
{
console.log('Server is running on port 3000');
});
Your data is a string and hence doesn't have those properties. You would need to JSON.parse it first.
But there is another issue, your code will break as soon as more data is returned, because you listen only for a single chunk of data. You have to add up all chunks (add to the existing chunks on every data event) and process the full data on the end event.
But in general the https.get method is very bare-bones, it will be a lot simpler to use a package like node-fetch:
// Creating the server of the weather app
const express = require('express');
const app = express();
const fetch = require('node-fetch')
app.get('/', (req, res) => {
const weatherPath = "https://api.weatherbit.io/v2.0/current?key=41c0f84d717a4764a26d144aa33a9443&city=melbourne,Australia"
// Calling the weather app
fetch(weatherPath)
.then(response => response.json())
.then(weatherData => {
// Getting the data from the weather app
const cityName = weatherData.data[0].city_name;
console.log(cityName);
}).catch(e => {
console.error('An error occured!', e);
});
res.send("The server is up and running on the web");
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Additional information
To address your comment:
I converted the data into a string though in the command line it looked like a JSON.
The term "JSON" is often used in a confusing way. Technically JSON (JavaScript Object Notation) is a serialization format, a string representation of an object or other basic JavaScript datatype (with limitations). The concept of a "live" object exists only in memory inside your script. So, the API is always sending you a string of characters. This string "is" JSON, i.e. uses JSON as method of representing structured data that, when parsed (!), can be turned back into a JavaScript object (in memory). So you are right that it looked like JSON since it is, but it's still a string at that point.
It's like sending you a blueprint (2D representation - JSON string) of a house (3D object - the original object). (You obviously can't send a house in a letter so people are sending blueprints (JSON) instead.) It looks like a house, because it is representing one, but you can't yet open its door (access a property) or something. At that point it's still just something printed on a piece of paper (a string) that people recognize as a blueprint (it is valid JSON). You have to first build an actual house (parse the JSON back into an object) from the blueprint.
(Of course this isn't made any better by using a variable name like json to represent the data parsed from JSON like it sometimes happens.)
I tried to hit the API and the response :
{"data":[{"rh":73,"pod":"n","lon":144.96332,"pres":1025.6,"timezone":"Australia\/Melbourne","ob_time":"2020-07-09 09:05","country_code":"AU","clouds":50,"ts":1594285500,"solar_rad":0,"state_code":"07","city_name":"Melbourne","wind_spd":1,"wind_cdir_full":"north-northwest","wind_cdir":"NNW","slp":1026.3,"vis":5,"h_angle":-90,"sunset":"07:16","dni":0,"dewpt":8.2,"snow":0,"uv":0,"precip":0,"wind_dir":348,"sunrise":"21:34","ghi":0,"dhi":0,"aqi":61,"lat":-37.814,"weather":{"icon":"c02n","code":"802","description":"Scattered clouds"},"datetime":"2020-07-09:09","temp":12.8,"station":"E5657","elev_angle":-20.02,"app_temp":12.8}],"count":1}
Edit :
i dont try your code in application before, i try by browser
This is mycode
// Calling the weather app
https.get(weatherPath, (response) => {
response.setEncoding('utf8')
let chunks = []
// Getting the data from the weather app
response.on('data', (d) => {
chunks.push(d);
});
response.on('end', () => {
let data = JSON.parse(chunks.join(''))
console.log(data.data[0].city_name)
});
});
weather.data is an array, so when you try access weather.data.city_name will be undefined. You must access weather.data[0].city_name.
I have solved using advance node module used popularly called axios
Have a look at Code,
// Creating the server of the weather app
const express = require('express');
const app = express();
const axios = require('axios');
app.get('/', (req, res) =>
{
//In production we do not need this
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
const weatherPath = "https://api.weatherbit.io/v2.0/current?key=41c0f84d717a4764a26d144aa33a9443&city=melbourne,Australia";
axios.get(weatherPath)
.then(function (response)
{
// handle success
let cityName = response.data.data[0].city_name;
console.log(cityName);
})
.catch(function (error)
{
// handle error
console.log(error);
})
.finally(function ()
{
// always executed
});
res.send("The server is up and running on the web");
});
app.listen(3000, () =>
{
console.log('Server is running on port 3000');
});
Related
I am following course for learning node. In process of API creation I have got strange error that doesn't break my code but yet I am not able to get the required output. Please check the codebelow. I am using Express V4 as the trainer recommended to use the same version as he was using. Now my codeis exact same but outputis not same. I am using Postman for this purpose, and the image of Postman is also attached.
The actual output was supposed to postthe data into my tours api but its adding "id": "[object Object]1" Please someone explain me why is that error and how to resolve it. Thanks in advance.
const express = require('express');
const fs = require('fs');
const app = express();
const port = 3000;
app.use(express.json());
const tours = JSON.parse(fs.readFileSync(`${__dirname}/dev-data/data/tours-simple.json`));
app.get('/api/v1/tours', (req, res) => {
res.status(200).json({
stauts: 'success',
results: tours.length,
data: {
tours // this is same as tours: tours
}
})
});
app.post('/api/v1/tours', (req, res) => {
// console.log(req.body);
const newId = tours[tours.length - 1].id + 1;
const newTour = Object.assign({id: newId}, req.body);
tours.push(newTour);
fs.writeFile(`${__dirname}/dev-data/data/tours-simple.json`, JSON.stringify(tours), err => {
res.status(201).json({
status: 'success',
data: {
tour: newTour
}
});
})
// res.send('Done');
})
app.listen(port, () => {
console.log(`Server is listening at port no ${port}`);
});
Postman
tours-simple.json file data
I tried simple post to my api but my data wasn't posted instead "id": "[object Object]1" was added.
In Postman change the request body format from body -> raw -> Javascript to body -> raw -> JSON, this will allow your server to parse the request data.
For the id, I'm not really sure what you are trying to do, tours[tours.length-1] is an object, if you would like to get the id of the last element in tours and increment it by 1 you should change it to this:
const newId = tours[tours.length - 1].id + 1;
I am trying to implement this code in my Node/Express server.
https://www.npmjs.com/package/#google-cloud/language?activeTab=readme
async function quickstart() {
// Imports the Google Cloud client library
const language = require('#google-cloud/language');
// Instantiates a client
const client = new language.LanguageServiceClient();
// The text to analyze
const text = 'Hello, world!';
const document = {
content: text,
type: 'PLAIN_TEXT',
};
// Detects the sentiment of the text
const [result] = await client.analyzeSentiment({document: document});
const sentiment = result.documentSentiment;
console.log(`Text: ${text}`);
console.log(`Sentiment score: ${sentiment.score}`);
console.log(`Sentiment magnitude: ${sentiment.magnitude}`);
}
The above code I guess cannot be on the frontend react side. So I tried to use it in my app.js file reference it from a GET request, but the server won't compile because it says require doesn't exits.
app.get('/sentiment', async (req, res) => {
res.header('Access-Control-Allow-Origin', '*');
console.log('sentiment was called'.green.inverse);
var results = quickstart(req.params);
res.send(results);
});
Not sure how to resolve require or if even implementing this quickstart correctly?
I was learning to build a weather app using Node (Express) + React. I successfully fetched weather data from open weather API.
However I was directly using the open weather API key in my React app like this const weatherURL = 'http://api.openweathermap.org/data/2.5/weather?q=london,uk&APPID=1234567qwerty';. Obviously this is not safe as it exposed the API key to the client. I thought about storing the API key in .env file, but according to [this answer][1], I should never store API key in .env file or .gitignore. The right way is to make a request to backend API and make an API call to backend and send the data back. I could not find out how to do it. Can anyone help?
Following is my node js code:
const express = require('express');
const cors = require('cors');
const app = express();
const SELECT_ALL_QUERY = 'SELECT * FROM `mySchema`.`myTable`;';
app.use(cors());
app.get('/', (req, res) => {
res.send('go to /myTable to see content')
});
const pool = require('./awsPool');
pool.getConnection((err, connection) => {
if (err) {
return console.log('ERROR! ', err);
}
if(!connection) {
return console.log('No connection was found');
}
app.get('/myTable', (req, res) => {
console.log(connection);
connection.query(SELECT_ALL_QUERY, (err, results) => {
if (err) {
return res.send(err)
}
else {
return res.json({
data: results
})
};
});
});
});
let port=process.env.PORT||4000;
app.listen(port, () => {
console.log(`App running on port ${port} `);
});```
[1]: https://stackoverflow.com/a/57103663/8720421
What the linked answer was suggesting is to create a route in your Node/Express backend API that will make the call to the weather API for you, instead of the front end. This way the request and your API key are not public-facing whenever your front end makes a call.
The method for doing this would essentially be the same as what you have done in React, making an HTTP request using a built-in or 3rd party library. This resource I just found has some information on how to do both.
The simplest pure http-request in node looks like this:
const http = require('http')
const url = 'http://api.openweathermap.org/data/'
http.request(url, callback).end()
function callback (weatherResponse) {
let jsonString = ''
weatherResponse.on('data', chunk => {
jsonString += chunk
})
weatherResponse.on('end', () => {
// Now you have the complete response and can do whatever you want with it
// like return it to your user `res.send(jsonString)`
console.log(jsonString)
})
}
Many people find it bulky to having to handle chunks and the whole asynchronous thing, so there are many popular npm modules, like: https://www.npmjs.com/package/axios. (And here's a list of other contenders https://github.com/request/request/issues/3143).
Also, it is normal to store API-keys in environment variables on the backend. It makes things easy if you ever try to dockerize your app, or just scale up to using two backend servers instead of one.
I found a solution based on #ippi answer, add the following part to the original code:
const request = require('request');
const url = 'http://api.openweathermap.org/data/2.5/weather?q=london,uk&APPID=1234567';
app.get('/weather', (req, res) => {
request(url, (error, response, body) => {
if (!error && response.statusCode == 200) {
var info = JSON.parse(body)
res.send(info);
}
})
})
The url can be stored in .env file and passed into the above code. The returned weather data can be viewed in JSON format at http://localhost:4000/weather. In React the weather data can be fetched via this localhost url.
EDIT: request is deprecated, so here is a solution using axios
app.get('/weather', (req, res) => {
axios.get(url)
.then(response => {res.json(response.data)})
.catch(error => {
console.log(error);
});
})
User Passport middleware for nodeJs/Express. They provide passport-headerapikey strategy using which you can create and authorize apiKeys. http://www.passportjs.org/packages/passport-headerapikey/
By my yet little understanding on how NodeJS/Javascript works, I believe it should be possible. Is it?
I would like to be able to send a function to a client based on http request, something like this (pseudo short code):
// Server
server.get("/function",()=>{
return function () => {
console.log('test)
}
// client
request ("server/function",(res)=>{
func = res.body
func()
// # result
// test
I have tried to convert to string to send the function from the server and convert back to an object on the client, but it return errors that I couldn't understand why as if I send a simple key value json object it works.
// server
const http = require('http');
const port = 3000;
const hostname = '127.0.0.1';
const server = http.createServer((req, res) => {
if (req.url === '/function') {
res.statusCode = 200;
function func () {
console.log('test')
}
res.end(func.toString())
}
});
server.listen(3000)
// client
const http = require('http');
const httpGet = url => {
return new Promise((resolve, reject) => {
http.get(url, res => {
res.setEncoding('utf8');
let body = '';
res.on('data', chunk => body += chunk);
res.on('end', () => resolve(body));
}).on('error', reject);
});
};
const call = async () => {
const body = await httpGet('http://localhost:3000/function')
console.log(`${body}`)
func = JSON.parse(body) // try to redefine function based on the response
}
const func = () => {
console.log('before')
}
func() // run the func defined here
call()
func() // run the function got from the request?
The idea is to have a client that will be able to execute almoust any code without the need of updating the client itself.
I'm not sure why you want to do this, it is really unsafe and you shouldn't have web applications that do this. But here goes:
Let's say you have a function:
function test() {
console.log("test")
}
And as string:
const funcString = test.toString()
// ^ is now "function test() {\n console.log(\"test\")\n }"
You need to first get the function body:
const funcBody = funcString.replace(/^[^{]+{/, '').replace(/}[^}]*$/, '')
// ^ is now "console.log(\"test\")"
Then create a function from it:
const newTest = Function(funcBody)
Now if you call newTest() you will see test printed in your console.
Note that the regular expression I have used to get the function body will not work on all functions. For example, for const test = () => 1 you will need a different regular expression.
I'm not sure this is the best idea.
Historically, the client-server relationship you are describing was inversed through remote procedure calls where the client would invoke a specific endpoint on a remote server. It sounds like your biggest draw towards having the client arbitrarily execute the code was the removal of updating the client code. What happens if you want to make backwards-incompatible changes to the server code? I think you will have better success using versioned API endpoints to execute code server-side based on client-side logic, which you will find many RPC and/or REST frameworks for within npm.
The ideia is to have a client that will be able to execute almoust any code without the need of updating the client itself.
One final thing to keep in mind are the security implications of this. What happens if I find your client and substitute in my malicious JavaScript?
I have a simple GET route setup with Koa and sql3lite that fetches from a json file some data, with the use of a wrapper.
A helper sql3lite wrapper exposes an initialize function for the main server index.js file to load into memory on startup:
index.js
const data = require('./data');
data.initialize();
const server = createServer();
server.listen(PORT, () => {
console.log(`server listening on port ${PORT}`);
});
The data.js file serves as a wrapper around the sqlite3 node library, so routes can call the connection() method individually.
data.js
const connection = new sqlite3.Database(':memory:');
function initialize() {
connection.serialize(() => {
connection.run('CREATE TABLE meter_reads (cumulative INTEGER, reading_date STRING, unit STRING)');
const { electricity } = sampleData;
electricity.forEach((data) => {
connection.run(
'INSERT INTO meter_reads (cumulative, reading_date, unit) VALUES (?, ?, ?)',
[data.cumulative, data.readingDate, data.unit],
);
});
});
}
module.exports = {
initialize,
connection,
};
The problem I am having is sending the data object back to a client or postman when calling the exported connection method:
koa route
const db = require('../data');
router.get('/meter/readings', async (ctx, next) => {
ctx.body = await db.connection.all('SELECT * FROM meter_reads', (err, data) => data);
});
GET request to route result:
{
"open": true,
"filename": ":memory:",
"mode": 65542
}
Interesting quirk:
By wrapping the data object, produced by the db.connection.all() method above with console.log(), I can print out the expected data on the node server. It's almost like ctx.body is screwing up something. I've been an express guy up until now so apologies if this is a rookie mistake!
Answered by GMS here:
https://github.com/mapbox/node-sqlite3/issues/1046#issuecomment-425643910
'all' does not return any value ( it returns the result by calling the given callback) so you are actually awaiting 'db.connection' in this line of code
there are some libraries based on node-sqlite3 providing promise based API and more
e.g
https://www.npmjs.com/package/sqlite
or mine:
https://www.npmjs.com/package/sqlite3orm