Backend post request only reads as "undefined" JS - javascript

I am pretty new to JS so please go easy on me.
Im trying to increment a value in a JSON file in my JS backend application. Whenever I increment the associated value by the key, it creates a new section "undefined"
Here is the entire application:
const { response } = require("express");
const express = require("express");
const fs = require("fs").promises;
const path = require("path");
const app = express();
const dataFile = path.join(__dirname, "data.json");
//support POSTing form data wuth url encoded
app.use(express.json());
app.get("/poll", async (req, res) =>{
//data is now the js object of the Json data json
let data = JSON.parse(await fs.readFile(dataFile, "utf-8"));
const totalVotes = Object.values(data).reduce((total, n) => total +=n, 0);
data = Object.entries(data).map(([label, votes]) => {
return{
label,
percentage: (((100 * votes) / totalVotes) || 0).toFixed(0) // or with 0 in the even that you divide by zero
}
});
res.json(data);
});
app.use(express.urlencoded({ extended: true}));
app.post("/poll", async (req, res) => {
const data = JSON.parse(await fs.readFile(dataFile, "utf-8"));
data[req.body.add]++;
await fs.writeFile(dataFile, JSON.stringify(data));
res.json(data);
});
app.listen(3000, () => console.log("Server is running ..."));
Here is my JSON file before I POST
{
"JavaScript":0,
"TypScript":10,
"Both":3}
Here is a picture of my POST request in Insomnia:
Here is my JSON file after a Post Requests
{
"JavaScript": 0,
"TypScript": 10,
"Both": 3,
"undefined": null
}
Here is is after a second POST request:
{
"JavaScript": 0,
"TypScript": 10,
"Both": 3,
"undefined": 1
}
I know it might be something very simple, but I am very inexperienced so any help would be greatly appreciated!
I tried including some additional middleware to enable bodyParsing as I saw in other posts but that did not fix the issue.
I also tried
console.logging(req.body)
,but that only printed "undefined".
EDIT:
I found the problem, I wasn't sending the information in Insomnia correctly, I need to click the form type and either format a JSON input or select the "Form URL Encoded"" Option. Otherwise it doesn't recognize the format.
Thank you for your help!

I tried your code in my device, it works fine on my device which means you are not properly sending post request.
And there is a bug in your logic of adding name , in which you increment the add property of data object but that property is not a number but a null so the number does not increments. Here is a simple fix...
app.post("/poll", async (req, res) => {
const data = JSON.parse(await fs.readFile(dataFile, "utf-8"));
let {add} = req.body;
if(data[add] === 0 || data[add]) data[add]++;
else data[add] = 0;
await fs.writeFile(dataFile, JSON.stringify(data));
res.json(data);
});

If req.body printed "undefined", req.body.add will also be equal to "undefined".
This is causing the error. I haven't used Insomnia but when you send your POST request your "body" needs to be something like this:
{
"add": "variableName"
}

Related

How to send data from NodeJS server side to the JS client side, only when data is ready?

On my website, when the user clicks on a button, some user's data will be stored in a database and after that I want the server to send notification data to the Javascript frontend file to change the UI.
Right now, the Js file (index.js) receives data right after the website loads (always false). I want it to be received only when the data is ready on the server.
I searched a lot but couldn't find an answer to my problem?
I appreciate any help :)
server.js
var requestValidation = false;
app.post("/", function(req, res){
var name = req.body.personName;
var email = req.body.personEmail;
var collabTopic = req.body.collabTopic;
const newUser = new User({ //mongoDB schema
name: name,
email: email,
collabTopic: collabTopic
});
newUser.save(function(err){ //adding data to mongoDB
if(!err){
requestValidation = true;
}
});
});
app.get("/succ", function(req, res){
res.json(requestValidation);
});
index.js
const url = "http://localhost:3000/succ";
const getData = async (url) => {
try {
const response = await fetch(url);
const json = await response.json();
console.log(json);
} catch (error) {
console.log(error);
}
};
getData(url);
I'm not sure this is completely the answer you're looking for, but it's definitely a tool/feature to consider as you rework your approach.
app.post("/", async (req, res) => {
let result = await INSERT MONGODB UPDATE OR INSERT FUNCTION;
res.render("YOUR TEMPLATE", result);
});
You probably can't plug and play this, but when you finish a MongoDB operation, it returns a json object with some details on whether or not there was success. For example, a MongoDB insert operation returns something like this (stored in the variable result that I created)
{ "acknowledged" : true, "insertedId" : ObjectId("5fd989674e6b9ceb8665c57d") }
and then you can pass this value on as you wish.
Edit: This is what tkausl referred to in a comment.
Here is an example if you want to pass the content of a txt file to the client with express and jquery:
in express:
app.get('/get', (req, res) => {
fs.readFile('test.txt', (err, data) => {
if (err) throw err;
return res.json(JSON.parse(data));
})
})
jquery in client side:
$.getJSON( "http://localhost:3000/get", function( data ) {
geojsondata1 = JSON.stringify(data)
}
now you can do anything you want with the variable data

Why downloading a file from node.js server multiple times results in empty files

I am glad to get some help.
Here is my problem:
I have built a web server with node.js that should send a csv file to the client when requested through a certain route. The csv file is created from json using the fast-csv package. The json data comes from a mongoDB and is processed with mongoose.
When I request this route once, it works fine. However, when it is requested a second time, an empty file is sent to the client. By the way, the headers reach the client correctly.
I have to restart the server to download my file again.
What did I try:
Basically, I have now lost track of everything I have tried. This behavior occurs both when using postman and when querying via the browser.
I've tried implementing promises in my handler function.
I've tried to unsubscribe
res somehow (but yes, that was a stupid approach).
I`ve tried to write the file into the fs and to send it on a second request. ...
Maybe one of you can tell what's going wrong here at first glance:
const { format } = require("#fast-csv/format");
const csvStream = format({ delimiter: ";", headers: true });
const router = express.Router();
router.route("/:collection/csv").get(requireModel, createCsv);
const csvFromDatabase = (data, res) => {
csvStream.pipe(res);
const processData = (data) => {
data.forEach((row) => {
const { _id, __v, ...newRow } = row._doc;
csvStream.write({ ...newRow });
});
csvStream.end();
};
processData(data);
};
const createCsv = async (req, res) => {
const { model } = req;
const items = await model.find({});
res.setHeader("Content-disposition", "attachment; filename=file.csv");
res.setHeader("Content-type", "text/html; charset=UTF-8");
csvFromDatabase(items, res);
};
Thank you very much for your help. I hope I didn't bore you with too stupid questions.
You need to recreate csvStream for each new request:
const csvFromDatabase = (data, res) => {
const csvStream = format({ delimiter: ";", headers: true });
csvStream.pipe(res);
…
};

I have a problem validating on update (express)

I'm trying to create a validator that will display an error message when a user tries to update a column that doesn't exist in the database schema. I'm using PostgreSQL. When I send a request with the correct allowed updates it returns an error: 'Invalid updates' What am I missing?
const updateQuestion = async (req, res) => {
const updates = Object.keys(req.body)
const allowedUpdates = ['title', 'text, questionCateg']
const isValidOperation = updates.every((update) => allowedUpdates.includes(update))
if (!isValidOperation) {
return res.status(400).send({
error: 'Invalid updates'
})
}
try {
const {
id
} = req.params;
const {
title,
text,
questionCateg
} = req.body
const updateQuestion = await pool.query("UPDATE Questions SET title = $1, text = $2, questionCateg = $3 WHERE id =$4",
[title, text, questionCateg, id]);
console.log(updateQuestion)
res.json('Question updated')
} catch (e) {
res.status(400).send()
}
}
Route:
router.patch('/questions/:id', updateQuestion)
Thanks in advance!
What do you see when you console.log(req.body) ? you should concentrate on that updates array.
You can use body-parser in your app.js or express.json() middleware
This way you can succesfully receive your items that you are sending to your backend.
You can send the updates in post request body with the key updates. Then you can parse it like req.body.updates (updates is an array)
You can use body parser like this
app.use(bodyParser.urlencoded({
extended: true
}));
or app.use(express.json())
you can see further information in this post express.json() and express.urlencoded()

How to take clientside Javascript arrays and POST through a Node.js API into a MongoDB database?

I have a single webpage that initially has two form inputs, one for a list of names and another for the title of a game. I've written some javascript/jquery that takes the X names and creates X more form inputs meant for each person's specific score. The javascript then creates the following variables upon the clicking of the names/scores form's submit button:
gameTitle = Monopoly
users = [Bob, Bill, Jim, Janet]
scores = [100, 110, 90, 80]
positions = [2, 1, 3, 4]
I then have a MongoDB schema set up as such:
const SessionSchema = new mongoose.Schema({
gameTitle: String,
users: [],
scores: [],
positions: []
});
And a Node.js handler as such:
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const timestamps = require('mongoose-timestamp');
router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());
const Session = require('./Session');
//Post a session to the database
router.post('/', function(req, res) {
Session.create({
gameTitle : req.body.gameTitle,
users : req.body.user,
scores : req.body.score,
positions : req.body.position
},
function (err, session) {
if (err) return res.status(500).send("There was a problem adding the information to the database");
res.status(200).send(session);
});
});
Using Postman I can see that posting works when I use this format:
Postman POST
Postman GET
How do I take the created javascript variables and, also upon the clicking of the names/scores form's submit button, POST them through the API and into the MongoDB database?
Apologies if I have missed any important information/code - I haven't fully wrapped my head around how the backend stuff works.
You need to register your Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const SessionSchema = new mongoose.Schema({
gameTitle: String,
users: [],
scores: [],
positions: []
});
module.exports = mongoose.model('session', SessionSchema);
And here you need to use the mongo schema model, like this:
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const timestamps = require('mongoose-timestamp');
router.use(bodyParser.urlencoded({ extended: true }));
router.use(bodyParser.json());
const SessionSchema = require('./Session'); // register the mongo model
const mongoose = require('mongoose');
const Session = mongoose.model('session');
//Post a session to the database
router.post('/', function(req, res) {
const new_session = {
gameTitle : req.body.gameTitle,
users : req.body.user,
scores : req.body.score,
positions : req.body.position
};
new_session.save((err, saved_session) => {
if(err) {
res.json(err);
} else {
res.json(saved_session);
}
});
});
Sounds like you have the backend working. What you're missing is the API request. Since your website is not under the same host:port than your API server, when doing it from the browser you'll face CORS issues. Let's get to that later:
First, you'll be making an API call. You can use axios or fetch. Let's go with fetch here:
fetch(url, {
body: JSON.stringify(yourJavascriptVariablesAsAnObject),
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
})
.then(response => {
// here you can check for status if you want.
// ...
return response.json(); // assuming server returns JSON.
})
.then(responseBody => {
// Do something with the server's response body
});
Now for the CORS problem, if your client app is from create-react-app or at least you're using webpack-dev-server, you can proxy request really easy.
If you're not, then you need to allow CORS on your nodeJS server. The simplest way is to use a library.
PS: CORS basically means you can't do requests from a browser to a service living in a different `url:port:, unless that service explicitly says it's ok.
A third option would be putting both UI and server project behind a Web server like Nginx and proxy the requests, but that sounds too complex for what you need.

Bodyparser is incorrectly parsing JSON?

I'm using the body-parser NPM module with Express to parse json on my server, but for some reason, the JSON is showing up incorrectly on the server. Here is my server code:
...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
...
app.route("/schedule")
.get(function(req, res) {
...
})
.post(function(req, res) {
var schedule = req.body.schedule;
console.log(req.body);
if(schedule) {
setSchedule(schedule);
res.status(200).end();
}
});
And my client code:
var schedule = {
entries: entries
};
var str = JSON.stringify(schedule);
console.log("Submitting schedule:",str);
post("/schedule", str)
.then((res) => {
this.completed(res.json);
})
.catch((res) => {
this.failed(res.text);
});
When I POST the data from the client, the client prints this:
Submitting schedule: {"entries":[1430014800000,1430055600000,1430104620000,1430146380000,1430194140000,1430236920000,1430283120000,1430326860000,1430371740000,1430416380000,1430460180000,1430505480000,1430548500000,1430594460000,1430636760000,1430683260000,1430725020000,1430772060000,1430813340000,1430860920000,1430901720000,1430949900000,1430990340000,1431039060000,1431079200000,1431128520000,1431168480000,1431218220000,1431258360000,1431308160000,1431349020000,1431398040000,1431440220000,1431487800000,1431531360000,1431577260000,1431622140000,1431666540000,1431712440000,1431755640000,1431802320000,1431844680000,1431891960000,1431933660000,1431981360000,1432022580000,1432070700000,1432111560000,1432159980000,1432200540000,1432249260000,1432289580000,1432338600000,1432378860000,1432428060000,1432468500000,1432517520000,1432558560000]}
Which appears to be valid JSON, but on the server, req.body is this:
{ '{"entries":': { '1430014800000,1430055600000,1430104620000,1430146380000,1430194140000,1430236920000,1430283120000,1430326860000,1430371740000,1430416380000,1430460180000,1430505480000,1430548500000,1430594460000,1430636760000,1430683260000,1430725020000,1430772060000,1430813340000,1430860920000,1430901720000,1430949900000,1430990340000,1431039060000,1431079200000,1431128520000,1431168480000,1431218220000,1431258360000,1431308160000,1431349020000,1431398040000,1431440220000,1431487800000,1431531360000,1431577260000,1431622140000,1431666540000,1431712440000,1431755640000,1431802320000,1431844680000,1431891960000,1431933660000,1431981360000,1432022580000,1432070700000,1432111560000,1432159980000,1432200540000,1432249260000,1432289580000,1432338600000,1432378860000,1432428060000,1432468500000,1432517520000,1432558560000]': '' } }
which is an object that's only key is {"entries": and the value for that key is an object that's only key is an array of timestamps that should be sent as the value to entries.
Any help would be greatly appreciated.
It appears the module I'm using for making requests (superagent) automatically stringifies data, so the issue went away when I stopped stopped using JSON.stringify.
In my case, the issue was that I was making the request from the front end using AngularJS, and the default POST format was set to url-encoded:
$httpProvider.defaults.headers.post = {'Content-type': 'application/x-www-form-urlencoded'};
By default, it's set to 'application/json', so I just removed this line, after which the JSON then resolved correctly.

Categories