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;
Related
I have currently set up a NodeJS "server" on GoogleCloud Platform server where I am listening and receiving POST HTTP calls.
The body of each call contains a JSON that I would like to parse and transfer to Google BigQuery.
For now, this is the code I've created to receive the POST request:
"use strict";
const express = require("express");
const messages = [];
async function startServer() {
const app = express();
app.use(express.json());
app.post("/receive2", (req, res) => {
res.status(200).json({ message: req.body });
//Here I should manager the json contained in req.body
});
const PORT = process.env.PORT || 8080;
logger.info({ PORT }, "bonjour");
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});
}
startServer();
The JSON is something similar to: https://gist.github.com/philipgiuliani/b63b22309d04048f0827
Yes, I am trying to save the receipts from In-App-Purchases made in my iOS app.
Can someone provide me a way to transfer the data of those receipts that I'm receiving to a BigQuery Table?
You can use NodeJS to append your csv data to a existing table or create a new table. I would recommend using Google Cloud Storage as a intermediate where you store the csv and simply point bigquery to the csv file which it should load. However you could also stream the data.
Stream:
let _count = await _BigQuery_
.dataset('dataset_name')
.table('table_name')
.query(`SELECT count(*) from table_name`)
.catch(e => console.error(e))
if (_count[0][0].count > 0)
console.log('no data')
Google Cloud Storage
Stream:
const gcs_file = Storage.file(gcs_path)
const gcs_write_stream = gcs_file.createWriteStream()
stream_from_csv_file
.on('error', (error) => {
gcs_write_stream.close()
})
.on('data', data => {
gcs_write_stream.write(row + '\r\n')
})
.on('end', function () {
gcs_write_stream.end();
loadFileToBigquery();
});
async function loadFIleToBigQuery(){
await _BigQuery_
.etl
.table('current_geoms_raw')
.load(gcs_file , {
sourceFormat: 'CSV',
skipLeadingRows: 1, // if headers are in csv
autodetect: false, // sometimes fails if true
location: 'US',
})
.catch(e => console.error(e))
}
My end goal is that I want to be able to create a test that satisfies the following statement:
verify that requests to valid URLs return a 200 HTTP status code
A valid URL for example would be /about-page or /jobs, basically any directory that I add in my content folder that contains a file with the extension /index.md.
This is my code so far:
app.js
const readFilePromise = util.promisify(fs.readFile)
app.get('/*', (req, res) => {
readFilePromise(path.join(__dirname, 'content', req.url) + '/index.md', 'utf8')
.then(data => {
convertData(data, res)
})
.catch(err => {
res.status(404).send('Page doesn\'t exist!')
})
})
const convertData = (data, res) => {
const convertedData = md.render(data)
readFilePromise(path.join(__dirname, '/template.html'), 'utf8')
.then(data => {
data = data.replace(/\{\{content\}\}/, convertedData)
res.send(data)
})
.catch(err => {
console.log(err)
})
}
app.listen(3000)
module.exports = app
After reading this article, it mentions that
Requests are asynchronous, which means you must be able to conduct asynchronous tests.
So I wrote the following test:
app.test.js
const app = require('./app.js')
const request = supertest(app)
const supertest = require('supertest')
it('Gets the test endpoint and returns a 200 status', async done => {
const res = await request.get('/*')
expect(res.status).toBe(200)
done()
})
When I run the test, it fails with a 404 status, rather than returning a 200 status. I thought this might be due to my app.js not being in the async/await style, so I changed app.js to:
const readFilePromise = util.promisify(fs.readFile)
app.get('/*', async (req, res) => {
try {
await readFilePromise(path.join(__dirname, 'content', req.url) + '/index.md', 'utf8')
} catch (err) {
res.status(404).send('Page doesn\'t exist!')
}
try {
const convertedData = md.render(data)
await readFilePromise(path.join(__dirname, '/template.html'), 'utf8')
data = data.replace(/\{\{content\}\}/, convertedData)
res.send(data)
} catch (err) {
console.log(err)
}
})
app.listen(3000)
module.exports = app
I tried running the test again, but it still fails with a 404. I think my set up within app.test.js is wrong, but I'm not sure exactly what, as I've tried using the various set ups as the article. How would I fix this?
Separately, when I try going to a URL using the async/await style in app.js, I get a ReferenceError: data is not defined error, but I'm not sure how to define data in the async/await format.
I explained here how to set up app for the test environment: supertest not found error testing express endpoint
You did not mention how you set the database environment, make sure your database is not empty. Then make your get request. but just checking status for get request is not enough because if your db is empty you will still get 200.
const response = await request(app).get("/route").send().expect(200);
expect(response.body.length).toBeGreaterThan(0)
Better approach would be connect to a different database, post your data first and then check the response
const response = await request(app).get("/api/tickets").send().expect(200);
expect(response.body.length).toEqual(2); // if you post two items
Also before you every test make sure you start with empty database inside beforeEach()
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/
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');
});
I am trying to make a URL shortener. I need to take a given URL as a parameter and send a request to that URL just to get the status code. If status = 200, I know I've got a functioning URL, and I'll go ahead and add it to the DB and shorten it.
Problem is, when I make that request, the connection times out.
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const nofavicon = require('express-no-favicons')
const Shortener = require('./shortener')
const app = express()
app.disable('x-powered-by')
app.use(cors())
app.use(nofavicon())
app.use(express.static(__dirname + '/static'))
mongoose.connect(
process.env.MONGODB_URI || 'mongodb://heroku_x7hcc5zd:39c8i70697o7qrpjn4rd6kslch#ds123371.mlab.com:23371/heroku_x7hcc5zd'
)
app.get('/url/:urlParam(*)', (request, response) => {
let urlParam = request.params.urlParam
let urlRegEx = /[A-Za-z]+[://]+[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&;?#/.=]+/g
if (urlRegEx.test(urlParam)) {
let shortRandomNum = Math.floor(Math.random() * 10000).toString()
// Shortener here refers to a mongoose Schema in external file
let lmao = new Shortener({
url: urlParam,
urlmao: 'localhost:8080/lol/' + shortRandomNum,
})
// Request header from passed URL to verify legitimacy
// Check statusCode and end request.
app.head(urlParam, (req, res) => {
let end = res.end
// Override standard res.end function with custom function
res.end = () => {
if (res.statusCode == 200) {
lmao.save((error) => {
if (error) {
response.send('Unable to write to collection')
}
})
console.log('pass')
response.json({lmao})
}
}
res.end = end
res.end()
})
} else {
// If passed URL does not satisfy regEx, return error message.
urlParam = 'unfunny url. http(s):// prefix required. check url and retry.'
console.log('invalid url')
response.json({
url: urlParam,
})
}
})
app.listen(process.env.PORT || 8080, () => {
console.log('live connection')
})
Most baffingly, the code shown here worked on Friday. Tested it last night, no dice. Any insight would be greatly, greatly appreciated.
app.head(urlParam, [Function]) doesn't make a request to the url, it defines a new route on your application so that it responds to HEADrequests on that url.
To check if the URL is alive you need to use another package to make requests. One of my favourites is Request. To use it simply replace app.head with request and add the require('request') to the top of your file.