Empty Object in req body on post request - node.js

I'm having trouble with getting the proper data out of req.body
When I fetch a request to the server I get this in my console (Ex: 'Wash dishes' as the value for the input)
// console log
{}
{ item: 'Wash dishes' }
an empty object first and then the item I specified.
html file
<form autocomplete="off" id="formform" method="POST">
<input type="text" name="item" placeholder="Add new item..." required id="inputinput" >
<button type="submit">Add Item</button>
</form>
test-w-fetch.js
let newForm = document.getElementById('formform');
newForm.addEventListener('submit', (evt) =>{
let item = document.getElementById('inputinput');
let todo = {item: item.value};
fetch('/todo', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: todo
}).then( (response) => {
return response.json();
}).then((data) =>{
location.reload();
}).catch((err) =>{
console.log(`There's an ${err}!!`);
});
return false;
});
Here's my POST route on the server
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.post('/todo', (req, res) => {
// Need to get data from the view and add it to mongodb
console.log(req.body);
let newItem = new Todo(req.body);
newItem.save( (err, data) =>{
if (err) throw err;
res.json(data);
});
});
Other option I have tried was taking error checking req.body.item property, storing it into a new object, and passing it to the database, but the empty object is still going through the body and getting saved to the database

One thing you could try to is use Postman to te to see if the issue is server or client side. Would help narrow down the troubleshooting surface area.

Related

POST request error: Path `title` and `body` are required

I'm trying to make a POST request to my server to create a new post (title, body) and save it in the db and I'm receiving a 200 status code however the title and body aren't being inserted into a post.
UPDATE: I have just changed return res.status(400).json({ error: errorHandler(err) }) in the controller method to res.send(err) and now I'm receiving a clear error message: The title and body paths are required.
How can I solve this?
CreatePost.js
class CreatePost extends React.Component {
constructor(props) {
super(props)
this.state = {
title: '',
body: ''
}
}
changeHandler = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
submitHandler = e => {
e.preventDefault()
axios.post(`${API}/blog/post/create`, this.state)
.then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
render() {
const {title, body} = this.state
return (
<div>
<form onSubmit={this.submitHandler}>
<input type="text" name="title"
onChange={this.changeHandler} value={title} />
<input type="text" name="body"
onChange={this.changeHandler} value={body}/>
<button type="submit">Submit</button>
</form>
</div>
)
}
}
export default CreatePost
controllers/post.js
exports.create = (req, res) => {
let post = new Post()
post.save((err, result) => {
if(err) {
return res.status(400).json({
error: errorHandler(err)
})
}
res.json(result)
})
}
routes/posts.js
router.post('/blog/post/create', create);
Did you try to call your endpoint from Postman or Insomnia (my favorite) to verify it is up and running? This is a great method to test your endpoints independently from your code.
Since you don't use HTTP 404 in your code, I suspect it is coming from the inner mechanisms of Express.
By the way, the verb POST and the 'create' part in your API uri are stating the same intent. You could get rid of this create part. This is considered one of the best practices.
Try with this.
axios({ url: `${API}/blog/post/create`, method: 'POST', data: this.state})
I've solved the issue. I wasn't actually inserting the title and body into the post object.
I destructured title and body from req.body:
const {title, body} = req.body and inserted the data into the object let post = new Post({title, body}).
controllers/posts.js
exports.create = (req, res) => {
const {title, body} = req.body
let post = new Post({title, body})
post.save()
.then(response => {
res.send(response)
.catch(err => {
res.send(err)
})
})
}

How do I send search result back to client side?

I'm making a web app that uses Spotify API now. I'd like to get a search keyword from client side and send its search result back to the client side. I'm making a post request with the search keyword to "http://localhost:4000/search_result" and send the result back to "http://localhost:4000/api" and then fetch the data. I get a 404 for the fetch call when I look into Network on the Chrome dev tool.
And I'd also like to know if there's way to make use of the search keyword from the post request for a get request in server.js.
Main.js
import React, { Component } from "react";
import SingerCard from "./SingerCard";
import axios from "axios";
export class Main extends Component {
constructor(props) {
super(props);
this.state = {
keyword: "",
artists: [],
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({ keyword: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
axios
.post(
"http://localhost:4000/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
// Accept: "application/json",
},
}
)
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
}
componentDidMount() {
fetch("http://localhost:4000/api")
.then((res) => res.json())
.then((artists) => {
this.setState({ artists });
})
.catch((err) => console.log);
}
render() {
return (
<div className="main">
<form onSubmit={this.handleSubmit}>
<label htmlFor="search">Search an artist: </label>
<span>
<input
type="search"
value={this.state.keyword}
onChange={this.handleChange}
name="keyword"
/>
<button type="submit" value="Submit">
Search
</button>
</span>
</form>
<br />
<div className="container">
{this.state.artists.map((elem) => (
<SingerCard
images={elem.images}
name={elem.name}
artists={this.state.artists}
/>
))}
{console.log("Arists are: " + this.state.artists)}
</div>
<br />
</div>
);
}
}
export default Main;
server.js
const express = require("express");
const SpotifyWebApi = require("spotify-web-api-node");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const port = 4000 || process.env.PORT;
require("dotenv").config();
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
// Create the api object with the credentials
var spotifyApi = new SpotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
});
// Retrieve an access token.
spotifyApi.clientCredentialsGrant().then(
function (data) {
console.log("The access token expires in " + data.body["expires_in"]);
console.log("The access token is " + data.body["access_token"]);
// Save the access token so that it's used in future calls
spotifyApi.setAccessToken(data.body["access_token"]);
},
function (err) {
console.log("Something went wrong when retrieving an access token", err);
}
);
app.post("/search_result", (req, res) => {
console.log(req.body.keyword);
spotifyApi.searchArtists(req.body.keyword).then(function (data) {
console.log(data.body);
var search_res = data.body.artists.items;
app.get("/api", (req, res) => {
res.json(search_res);
res.end();
});
res.end();
}),
function (err) {
console.log(err);
};
});
app.listen(port, () => console.log(`It's running on port ${port}`));
Your server side routes should be configured once, not on the fly. When you call app.post and app.get you are configuring a route and providing a callback that will be used every time a request comes in that matches that route. In your code, you create a post route, and then inside the handler you create a get route. Don't do that.
HTTP Servers are meant to be stateless. This means each request (post, get) know nothing about one another. If you want to share information between, you do it via a database (in-memory, redis, nosql, sql, etc...), where each route is using only the parameters passed to it.
For example, the POST route creates an id for the search results. The POST route returns this id. The GET route when called, should receive this same id and look up the results. Of course, this is only necessary for really long processes, typically job queues. For your specific case, you only need one route.
app.post("search_result", (req, res) => {
console.log(req.body.keyword);
spotifyApi
.searchArtists(req.body.keyword)
.then(
function(data) {
console.log(data.body);
var search_res = data.body.artists.items;
res.json(search_res);
res.end();
},
function (err) {
console.log(err);
res.status(500).send(err);
}
);
});
And don't forget to remove your componentDidMount code since you only need to call the POST route.
handleSubmit(e) {
e.preventDefault();
axios
.post(
"http://localhost:4000/search_result",
{
keyword: this.state.keyword,
},
{
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
// Accept: "application/json",
},
}
)
.then(function (res) {
console.log(res);
this.setState({ artists });
})
.catch(function (err) {
console.log(err);
});
}

Upload client file to website through node

I'm trying to achieved the following:
Client has to fill a form and submit it to the node server
If client has a business card, snap a picture and it updates the form.
I have the API to translate image to json.
But I struggle to upload that image through node.
I created an html form with an input=file.
When input is changed, Ajax is called to upload that image.
On node, the request is received and basically transferred to that other website (camcard).
Json response should be sent back to client.
I just can't achieve that..
Any idea?
Here is some code. I do a post in 2 ways (regular and Ajax)
Html client:
<form method='POST' id="formCard"
enctype='multipart/form-data'>
<input type=file class="btn btn-outline-primary" name=upfile
size=20 id="cardSelector" onChange="changeFile()">
</form>
<form method='POST' action='/postCardPic'>
Please specify a JPG file<br>
<input type=file name=upfile size=20><br><br>
<input type=submit value='Upload'>
</form>
<script>
function changeFile() {
var formData = new FormData();
formData.append( 'file', $( '#cardSelector' )[0].files[0] );
$.ajax({
data: formData,
enctype: 'multipart/form-data',
processData: false,
contentType: false,
type: "POST",
url: "/postCardPic",
success: function (response) { // on success..
console.log(response); // update the DIV
},
complete: function (data) {
hideLoader();
}
});
}
</script>
On the server side:
App.use(bodyParser.urlencoded({
extended: false,
limit: '50mb',
parameterLimit:'50000'
}));
app.use(bodyParser.json({
extended: false,
limit: '50mb',
parameterLimit:'50000'
}));
app.post('/postCardPic', function(req, response) {
var urlCamCard='http://whatever.com'
// var form = new Form();
// form.parse(req, function(err, fields, files) {
// res.writeHead(200, {'content-type': 'text/plain'});
// res.write('received upload:\n\n');
// res.end(util.inspect({fields: fields, files: files}));
// });
// form.on('file', function(name,file) {
// //stream it to localhost:4000 with same name
// fs.createReadStream(file.path).pipe(request.post(urlCamCard))
// });
request.post({
url:urlCamCard, form:req.body}, function optionalCallback(err, httpResponse, body) {
if (err) {
return console.error('upload failed:', err);
}
console.log('Upload successful! Server responded with:', body);
});
response.end();
});
Log in both cases is the same:
Upload successful! Server responded with: ErrorCode: -1 ErrorMsg: Image data size not supported
I found the solution.
Here's is the code on the server side:
App.post("/postCardPic", function(req, res) {
try {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {});
form.on("file", function(name, file) {
var formData = {
file: {
value: fs.createReadStream(file.path),
options: {
filename: file.originalFilename
}
}
};
request.post(
{
url: theurl,
formData: formData
},
function optionalCallback(err, httpResponse, body) {
if (err) {
return console.error("upload failed:", err);
}
res.end(body);
}
);
});
} catch (err) {
console, log(err);
res.end();
}

NodeJS - AJAX POST 404 (Not Found)

I am trying to send values from one file to another on click of items displayed.
While doing so, I am getting the error:
POST http://localhost:4000/todo/addToCart 404 (Not Found) jquery-3.3.1.js:9600
My app.js file:
//More codes above to set-up express and all
app.use(express.static('./public'));
todoController(app); //give todocontroller the reference to express
app.listen(4000); //listen on a port
console.log('server is running');
Controller:
module.exports = function(app) {
app.get('/todo', function(req, resp) {
Todo.find({}, function(err, data) {
if (err) throw err;
console.log('get method');
resp.render('todo', {
todos: data
});
});
});
//Few More Code
app.post('/todo', urlencodedParser, function(req, resp) {
console.log('post method');
resp.render('addToCart', {
data: req.body
});
});
};
Model for data interaction:
$('li').on('click', function() { //when user clicks on an item in the list
var item = $(this).text().replace(/ /g, "-"); //traps the item user clicked on
alert(item);
$.ajax({
type: 'POST',
url: '/todo/addToCart', //+item append that item to the url
success: function(item) {
location.reload(); //refresh the page
}
});
});
Parent ejs:
<div id="todo-table">
<form id="todoForm" method="post" action="/todo">
<input type="text" name="item" placeholder="Add new Item..." required />
<button type="submit">Add Item</button>
<ul>
<% for (var i=0;i<todos.length; i++){ %>
<li>
<%=todos[i].item%>
</li>
<% } %>
</ul>
</form>
</div>
Child ejs(to which I need to re-direct):
<div id="itemSelect">Selected Item:
<form id="addToCart" method="post" action="/addToCart">
<button type="submit" id="btnCheckOut">Check out</button>
<%=data.item%>
</form>
</div>
Please help. I am new, kindly point out my mistake.
Many thanks.
The route you created on your nodejs server here:
app.post('/todo', urlencodedParser, function (req, resp) {
console.log('post method');
resp.render('addToCart', { data: req.body });
});
Matches all the POST requests made to the /todo endpoint, not the /todo/addToCart which doesnt exist. This is why you obtain a 404.
Your ajax request should be like so:
$('li').on('click', function () {
var item = $(this).text().replace(/ /g, "-");
alert(item);
$.ajax({
type: 'POST',
url: '/todo', // 'addToCart' has been removed from the path
success: function (item) {
location.reload();
}
});
});

Proxy error: Could not proxy request /users from localhost:3000 to http://localhost:3001/

I'm sending some data from my ReactJS front-end application to my node/express backend, however, whenever I send the data, I get the error message mentioned in the title.
This is my ReactJS code:
class App extends React.Component {
constructor(props) {
super(props);
//states and binding functions
}
fetchData () {
fetch('/users')
.then(res => res.json())
.then(users => this.setState({users}));
}
addUser (event) {
let fileData = new FormData();
fileData.append("file", this.state.filesToBeSent);
axios.post('adduser',
querystring.stringify({
entry: this.state.currname,
passwrd: this.state.currpasswrd,
fileData : fileData
})
)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
componentDidMount() {
this.fetchData();
}
render() {
return (
<div className="App">
<form onSubmit={this.addUser} encType="multipart/form-data">
<label>New username:</label>
<input value={this.state.currname} onChange={this.handleChange}></input>
<input value={this.state.currpasswrd} onChange={this.handlePassword} />
<input type="file" name="file" onChange={this.handleFile} />
<button type="submit" >Add</button>
</form>
<h1>Current Users: </h1>
{this.state.users.map((name, n) =>
//map through user-array and display names
)}
</ul>
</div>
);
}
}
(Sorry if it's a lot of code, I shortened it as much as possible but I wasn't sure which parts would be relevant to the question).
Here is how I receive the data in node and how I save parts of it to my db:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./uploads/");
},
filename: (req, file, cb) => {
const newFilename = `${uuidv4()}${path.extname(file.originalname)}`;
cb(null, newFilename);
},
})
const upload = multer({ storage });
router.post("/", upload.single("file"), (req, res) => {
res.send(req.file + " and exit.");
var newUser = new Todo();
newUser.username = req.body.entry;
newUser.passwrd = req.body.passwrd;
newUser.save(function (err) {
if(err)
res.send(err);
res.send('User added successfully!');
});
});
This is where it gets weird. The application works perfectly, until I insert upload.single("file"), however, I couldn't seem to figure out why. I didn't have any problems when I just had text inputs, even when I created the FormData() etc. it still worked fine until I implemented that.
I tried looking it up and to implement answers posted on here, however, nothing seems to help.
On the screen I get the following error message: Unhandled Rejection (SyntaxError): Unexpected token P in JSON at position 0
When I check the terminal I receive the error message mentioned in the title. I tried removing the content-headers (not sure what that would do, but the tutorial I was following to implement the file upload did not use content-headers, that's why I tried to remove them.
Does anyone know how to fix this error?
Edit: The error message in the terminal also contains ECONNRESET. I followed the link in the terminal https://nodejs.org/api/errors.html#errors_common_system_errors but I'm still not sure how I can fix that.
I suggest you to append all fields to FormData object, and do not convert the submitted data to json string:
let fileData = new FormData();
fileData.append("entry", this.state.currname);
fileData.append("passwrd", this.state.currpasswrd);
fileData.append("file", this.state.filesToBeSent);
axios.post('adduser', fileData)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

Resources