Post request saving only versionKey with Mongoose and Express - node.js

I am trying to save a number and a few string values to a MongoDB and even though the code makes perfect sense to me, returns no errors and creates an entry in the database, all I get is just a versionKey, something like this:
{
"_id": {
"$oid": "58052711f319bc041c5ebdac"
},
"__v": 0
}
I thought I'd try just saving the number and the title first to see if they get saved, but they don't, as you can see above.
Every RESTful API and Express "post" request tutorial and answer I find seems to do it differently!
Also, is it supposed to show the res.json in plaintext for me to format or render somehow, or is it supposed to show the value of the property message:?
Here is the code
// modules =======================================================
const express = require('express')
const app = express()
const mongoose = require('mongoose')
// configuration =================================================
const db = require('./config/db')
// mongoose ======================================================
mongoose.connect(db.url)
const PostSchema = new mongoose.Schema({
number:Number,
title:String,
body:String,
images:String
}, { collection: 'posts' })
const posts = mongoose.model('Post', PostSchema)
// routes ========================================================
app.post('/api/post', function(req, res) {
var post = new posts()
post.number = req.body.number
post.title = req.body.title
post.save(function(err) {
if (err) res.send(err)
res.json({ message: 'Post saved'})
})
})
Here is my HTML5 form
<form action="/api/post" enctype="multipart/form-data" method="post">
<label for="number">Post Number:</label>
<input type="number" name="number" size="2" placeholder="required" required /><br />
<label for="title">Title:</label>
<input type="text" name="title" placeholder="optional" /><br />
<label for="body">Text:</label><br />
<textarea name="body" cols="80" rows="20" placeholder="optional"></textarea><br />
<label for="images">Images:</label>
<input type="text" name="images" placeholder="optional" />
<span class="form-hint">Comma separated file names, e.g. image1.jpg,image2.jpg,image3.png</span><br />
<input type="submit" value="Submit" />
</form>
My middleware that comes just before the routes in server.js
// middleware ====================================================
app.use(bodyParser.json()) // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })) // parse application/vnd.api+json as json
app.use(bodyParser.urlencoded({ extended: true })) // parse application/x-www-form-urlencoded
app.use(methodOverride('X-HTTP-Method-Override')) // override with the X-HTTP-Method-Override header in the request
app.use(express.static(__dirname + '/public')) // set static files location

First question, the encoding must match what you have Express parsing (in this case it wasn't multipart/form-data but application/json, 'application/vnd.api+json', and application/x-www-form-urlencoded. Removing the encoding type you were specifying fixes that.
Second, the response will be a simple JSON object:
{
"message": "Post saved"
}

Related

Express.js req.body is null when form submitted

I have a simple html form:
<div class="signup-form">
<h2>Signup for an account</h2>
<form method="POST" action="/submit-signup">
<input type="text" title="username" placeholder="username" />
<input type="password" title="username" placeholder="password" />
<button type="submit" class="btn">Submit</button>
</form>
</div>
and an index.js with routes:
var bodyParser = require('body-parser')
const express = require('express')
const app = express()
const port = 3000
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
const Pool = require('pg').Pool
const pool = new Pool({
user: 'user',
host: 'localhost',
database: 'app',
password: 'password',
port: 5432,
})
app.get('/', function(req, res) {
res.sendFile(__dirname +'/index.html');
});
app.get('/login', function(req, res) {
res.sendFile(__dirname +'/login.html');
});
app.get('/signup', function(req, res) {
res.sendFile(__dirname +'/signup.html');
});
app.post('/submit-signup',(req, res) => {
console.log(req.body)
const username = req.body.username
const password = req.body.password
pool.query('INSERT INTO users (username, password) VALUES ($1, $2)', [username, password], (error, results) => {
if (error) {
throw error
}
response.status(201).send(`User added with ID: ${result.insertId}`)
})
})
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
Every time the form is submitted with answers the request body logs as {} and the error is thrown from the query because of the null value (the column in my database is set to not-null). I've tried most fixes from old stack overflow posts, so any help is appreciated
You are using the body-parser middleware. This middleware by default will try to parse every http body into a js-object based on a given json value.
As you are sending your data over an HTML form this data is not in JSON representation, but in the data generated by the HTML form.Mozilla Specs
To make this work you either need to iplement a way to read the body sent by the HTML form or use a more modern approach by sending a (REST) JSON request to your express server.
To make your code work use body-parser.urlencoded() as HTML form data is an url encoded text Ref to existing Stackoverflow answer
To make your form a bit more modern, use a JavaScript framework and follow a client-server pattern. Have a look at this tutorial REST tutorial with express
Your problem cuz you don't use a name tag in HTML form.
<div class="signup-form">
<h2>Signup for an account</h2>
<form method="POST" action="/submit-signup">
<input type="text" name="username" title="username" placeholder="username" />
<input type="password" name="password" title="password" placeholder="password" />
<button type="submit" class="btn">Submit</button>
</form>
</div>
Add name attribute in your html input field

checkbox array returns the last checked value in nodejs, not whole array

I am trying to take through req.body the checked values from checkbox. If i check only one there is no problem and on req.body there is an object with the value. If i check more than one then it returns the last one in a row i check. I am using express ejs and express.json() as a body parser.
This is a section from my app file where i config the express.json()
const express = require("express");
const path = require("path");
const rateLimit = require("express-rate-limit");
const helmet = require("helmet");
const mongoSanitize = require("express-mongo-sanitize");
const xss = require("xss-clean");
const hpp = require("hpp");
const cookieParser = require("cookie-parser");
const throwsAnError = require("./utils/throwsAnError");
const checkAuth = require('./middleware/check-auth');
const app = express();
if(process.env.NODE_ENV !== "test"){
const limiter = rateLimit({
max: 100,
windowMs: 60 * 60 * 1000,
message: "Too many request, please try again in an hour"
});
app.use("/api", limiter);
}
app.set("view engine", "ejs")
app.set("views", path.join(__dirname, "views"))
app.use(express.static(path.join(__dirname, "public")));
app.use(cookieParser());
app.use(helmet());
// For security reasons we accept body up to 10kb
app.use(express.json({ limit: '10kb' }));
app.use(express.urlencoded({ extended: false }));
// Data sanitization (Cleaning the income data from malicious code)
// NoSQL data injection
app.use(mongoSanitize());
// Data sanitization against XSS
app.use(xss());
//Preventing HTTP parameter poloution
app.use(hpp());
The router is this:
router.get("/offers/masoutis-offers", authController.isLoggedIn, viewController.masoutisOffers);
router.post("/offers/masoutis-offers", authController.isLoggedIn, viewController.masoutisOffers);
The controller is this:
exports.masoutisOffers = async function(req, res) {
console.log(req.body);
const products = await getOffers(req);
res.status(200).render("displayProducts", {
title: "Επιλεγμένες Προσφορές",
products
});
}
async function getOffers(req) {
const page = parseInt(req.query.page || 1);
const sort = req.query.sort || "discountPrice";
const itemsPerPage = 10;
const totalProducts = await Offers.countDocuments();
const products = await Offers.find()
.skip((page - 1) * itemsPerPage)
.limit(itemsPerPage)
.sort(sort);
return {
products,
sort,
currentPage: page,
hasNextPage: itemsPerPage * page < totalProducts,
hasPreviousPage: page > 1,
nextPage: page + 1,
previousPage: page - 1,
lastPage: Math.ceil(totalProducts / itemsPerPage)
}
}
And the test form i am using is this:
<form action="" method="post">
<input type="checkbox" id="hobby" name="stuhobbies" value="sing"/> <strong>Sing</strong>
<input type="checkbox" id="hobby" name="stuhobbies" value="guitarplay"/> <strong>GuitarPlay</strong>
<input type="checkbox" id="hobby" name="stuhobbies" value="cricket"/> <strong>Cricket</strong>
<input type="checkbox" id="hobby" name="stuhobbies" value="football"/> <strong>Football</strong>
<button type="submit">submit</button>
</form>
If for example i check all values, as a return from the console.log(req.body) i receive
{ stuhobbies: 'football' } instead of an array of all values
Use express's extended URL-encoded body parsing, like this
app.use(express.urlencoded({extended: true}));
or, in older versions of express, via the body-parser package like this
const bodyParser = require('body-parser')
...
app.use(bodyParser.urlencoded({ extended: true}))
Then you can put this sort of thing in your checkbox html.
<form action="" method="post">
<input type="checkbox" id="checkboxsing" name="voice[]" value="sing"/> Sing
<input type="checkbox" id="checkboxshout" name="voice[]" value="shout"/> Shout
<input type="checkbox" id="checkboxwhisper" name="voice[]" value="whisper"/> Whisper
<input type="checkbox" id="checkboxmumble" name="voice[]" value="mumble"/> Mumble
<button type="submit">submit</button>
</form>
And you'll see this in your req.body object in your .post() handler.
{
"voice": [
"sing",
"shout"
],
}
Notice that the id attribute on each of the checkboxes in my example is different. Creating two DOM objects (<div>, <input>, whatever) with the same id is invalid HTML. The browser doesn't complain, but it prevents you from doing this to label your checkboxes.
<form action="" method="post">
<input type="checkbox" id="checkboxsing" name="voice[]" value="sing"/>
<label for="checkboxsing">Sing</label>
<input type="checkbox" id="checkboxshout" name="voice[]" value="shout"/>
<label for="checkboxshout">Shout</label>
<input type="checkbox" id="checkboxwhisper" name="voice[]" value="whisper"/>
<label for="checkboxwhisper">Whisper</label>
<input type="checkbox" id="checkboxmumble" name="voice[]" value="mumble"/>
<label for="checkboxmumble">Mumble</label>
<button type="submit">submit</button>
</form>
What I did and could not take the array was that I had installed a package called hpp https://www.npmjs.com/package/hpp. It removes any array from req.body and puts only the last value from the array. It has an option for whitelisting and this is the way i solved it.

How to post data from react to express

I am trying to create a book app i have react on the front and node js on the backend. When i tried to create in backend its say Cannot POST /create.What do i have to do ,the folder is divided into front end and backend. i am using axios.I am new to react js please help.How can i pass data from a form in react to express to save.
this is the react component create
import React, {Component} from 'react';
class Create extends Component{
render(){
return(
<div>
<br/>
<div class="container">
<form action="http://127.0.0.1:3000/create" method="post">
<div style={{width: '30%'}} class="form-group">
<input type="text" class="form-control" name="BookID" placeholder="Book ID"/>
</div>
<br/>
<div style={{width: '30%'}} class="form-group">
<input type="text" class="form-control" name="Title" placeholder="Book Title"/>
</div>
<br/>
<div style={{width: '30%'}} class="form-group">
<input type="text" class="form-control" name="Author" placeholder="Book Author"/>
</div>
<br/>
<div style={{width: '30%'}}>
<button class="btn btn-success" type="submit">Create</button>
</div>
</form>
</div>
</div>
)
}
}
export default Create;
this index.js in the backend
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var cors = require('cors');
app.set('view engine', 'ejs');
//use cors to allow cross origin resource sharing
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
var books = [{
"BookID": "1",
"Title": "Book 1",
"Author": "Author 1"
},
{
"BookID": "2",
"Title": "Book 2",
"Author": "Author 2"
},
{
"BookID": "3",
"Title": "Book 3",
"Author": "Author 3"
}
]
app.get('/home', function (req, res) {
console.log("Inside Home Login");
res.writeHead(200, {
'Content-Type': 'application/json'
});
console.log("Books : ", JSON.stringify(books));
res.end(JSON.stringify(books));
})
app.post('/create', function (req, res) {
var newBook = {
"BookID": req.body.BookID,
"Title": req.body.Title,
"Author": req.body.Author
}
books.push(newBook)
console.log(books);
})
//start your server on port 3001
app.listen(3001);
console.log("Server Listening on port 3001");
There were a few errors. Here is some updated code and a description of what was going on:
React App.js:
import React, { Component } from 'react';
import axios from 'axios';
class Create extends Component {
constructor(props) {
super(props);
this.state = {
bookID: '',
bookTitle: '',
bookAuthor: '',
};
}
handleInputChange = e => {
this.setState({
[e.target.name]: e.target.value,
});
};
handleSubmit = e => {
e.preventDefault();
const { bookID, bookTitle, bookAuthor } = this.state;
const book = {
bookID,
bookTitle,
bookAuthor,
};
axios
.post('http://localhost:3001/create', book)
.then(() => console.log('Book Created'))
.catch(err => {
console.error(err);
});
};
render() {
return (
<div>
<br />
<div className="container">
<form onSubmit={this.handleSubmit}>
<div style={{ width: '30%' }} className="form-group">
<input
type="text"
className="form-control"
name="bookID"
placeholder="Book ID"
onChange={this.handleInputChange}
/>
</div>
<br />
<div style={{ width: '30%' }} className="form-group">
<input
type="text"
className="form-control"
name="bookTitle"
placeholder="Book Title"
onChange={this.handleInputChange}
/>
</div>
<br />
<div style={{ width: '30%' }} className="form-group">
<input
type="text"
className="form-control"
name="bookAuthor"
placeholder="Book Author"
onChange={this.handleInputChange}
/>
</div>
<br />
<div style={{ width: '30%' }}>
<button className="btn btn-success" type="submit">
Create
</button>
</div>
</form>
</div>
</div>
);
}
}
export default Create;
You were getting errors for using class and not className. class is a reserved word in react.js and should not be used.
You were using the default post method which I would not recommend. I split the post out into it's own action and used the common library axios to make the CORS post call. I also created a function to handle the input changing on every key press with react.js.
I added state to your component. This is common when there are form inputs to store them in state. I also changed the name of your variables to be title case which is the common way to write code variables.
Node.js index.js:
const express = require('express');
const logger = require('morgan');
const cors = require('cors');
const app = express();
//use cors to allow cross origin resource sharing
app.use(
cors({
origin: 'http://localhost:3000',
credentials: true,
})
);
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
let books = [];
app.get('/home', function(req, res) {
console.log('Inside Home Login');
res.writeHead(200, {
'Content-Type': 'application/json',
});
console.log('Books : ', JSON.stringify(books));
res.end(JSON.stringify(books));
});
app.post('/create', function(req, res) {
const newBook = {
BookID: req.body.bookID,
Title: req.body.bookTitle,
Author: req.body.bookAuthor,
};
books.push(newBook);
console.log(books);
});
//start your server on port 3001
app.listen(3001, () => {
console.log('Server Listening on port 3001');
});
You weren't parsing the body of the req, so it was coming back as
undefined. I added app.use(express.json()); and
app.use(express.urlencoded({ extended: false })); which should
take care of most of the issues.
I updated the req.body variables to match those coming over from
React.
I added the module morgen which you see here app.use(logger('dev')); this is helpful by showing all your requests and statuses for dev purposes. In this case, it was showing that you were getting a 500 (internal server error) because express couldn't read bookID of undefined (because the body wasn't being parsed).
This should be working now, let me know if you have any problems.
I don't use express some so the details may not apply.
In essence you will have to send a network request to your server.
How you do this is up to you, The most common ways are with axios(a library) or with vanilla js with the fetch api.
I would just use the fetch api.
it takes two parameters an url and the options.
so it should be called like this fetch(url,options)
so in your case it would be fetch('localhost:3001/create, options)
What should be in the options.
I just suggest you look at the fecth MDN docs here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
but your case you will need to pass an object with the method property set to post and the new book you want to data property set to a JSON serialized object of the book you want to create.
for example
let book ={
BookId:1,
Title: "coolBook",
Author: "Me"
}
fetch("localhost:3001/create",
{
method: "post",
data: JSON.stringify(book)
}
When passing the books a string instead of an object you will likely have to take that string and parse it as an object on the server so that you express /create handler looks more like:
app.post('/create', function (req, res) {
var newBook = JSON.parse(req.body.data)
books.push(newBook)
console.log(books);
})
On the react side you need to create an event handler that calls the above fetch function. I recommend you watch a react/express tutorial though as I can;t really cover all the thing required here in a stack overflow question such as: using and validating forms in react, error handling, async/await and so on.
All the Best! hope that was slightly helpful
You should have some kind of response on your /create endpoint
For example:
app.post('/create', function (req, res) {
var newBook = {
"BookID": req.body.BookID,
"Title": req.body.Title,
"Author": req.body.Author
}
books.push(newBook)
console.log(books);
res.status(201).json({"some":"response"})
})

Formidable always returns empty fields and files when I upload a file in Node.js

Basically I want to upload a csv file from local computer and parse it in the backend to do required operations. I'm attaching the csv file in the front end. Checked that its not empty. But I'm unable to fetch the same in the server.
Is there something I'm missing or doing in the wrong way?
Here is what I have tried till now.
Front end code:
<form id="myForm" method="POST" enctype="multipart/form-data" action='/testcsv' >
<input type="file" id="file" />
<input type="submit" value="Submit">
</form>
Backend Code:
var express = require('express');
var methodOverride = require('method-override');
var http = require('follow-redirects').http;
var formidable = require('formidable');
var app = express();
const fs = require('fs');
app.use(methodOverride('_method'));
var bodyParser = require('body-parser');
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
app.post('/testcsv', requireLogin, function(req, res) {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log(err);
console.log(fields);
console.log(files);
});
});
Log Output:
null
{}
{}
This problem is caused by Frontend code, it has nothing to do with Backend code (formidable).
For the following console.log statement:
console.log(err);
console.log(fields);
console.log(files);
err is null because there is no error.
fields is {} because in the form, all input fields are file selector. fields in formidable only indicate plain input field, such as <input type="text">.
files is {} because the name of file selector in the form is missing.
To get expected value, here is an example form:
<form id="myForm" method="POST" enctype="multipart/form-data" action='/testcsv' >
<input type="text" name="testtext" />
<input type="file" id="file" name="testfile" />
<input type="submit" value="Submit">
</form>
The console.log result for above form would be:
null
{ testtext: '....' }
{ testfile: File....}

nodejs throws cannot post error

I am trying to post the data from html form but it is showing an error "cannot POST".
This is my code:
controller/InquiryDetails.js:
var mongoose = require('mongoose'),
InquiryDetails = mongoose.model('InquiryDetails');
exports.add = function(req, res) {
InquiryDetails.create(req.body, function (error, details) {
if (error) return console.log(error);
return res.send(details);
});
}
routes/Inquiry.js
var express = require('express');
var router = express.Router();
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({extended:true}));
app.post('/InquiryDetails', function(req,res,err){
if(err) console.log(err);
res.json(req.body);
console.log(req.body);
});
module.exports = router;
model/InquiryDetails.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var myskyll = new Schema({
name: String,
email: String,
state: String,
country: String,
school: String,
profession: String,
phone: Number
});
mongoose.model('InquiryDetails', myskyll);
app.js:
var express = require('express');
var mongoose = require('mongoose');
var request = require('request');
var connect = require('connect');
var serveStatic = require('serve-static');
connect().use(serveStatic(__dirname+"/index.html")).listen(8080);
var mongoUri = 'mongodb://localhost/myskyll';
mongoose.connect(mongoUri);
var db = mongoose.connection;
db.on('error', function () {
throw new Error('unable to connect to database at ' + mongoUri);
});
var app = express();
console.log("connection successfull");
app.configure(function(req,res){
app.use(express.bodyParser());
});
app.use(express.static(__dirname + "/" ));
require('./models/InquiryDetails');
require('./routes/Inquiry');
app.listen(3000);
console.log('Listening on port 3000...');
index.html:
<form method="post" id="form1" action="InquiryDetails">
Your Name:<span class="imp">*</span><br/><input type="text"
name="name" placeholder="Firstname Lastname" class="textbox"
autofocus required pattern="[A-Za-z-0-9]+\s[A-Za-z-'0-9]+"
title="'Firstname' <space> 'Lastname'"/> <br/><br/>
Email:<span class="imp">*</span><br/><input
type="email" name="email" placeholder="Email" class="textbox"
required/><br/><br/>
city:<span class="imp">*</span><br/><input type="text" name="city"
placeholder="city" class="textbox" required/><br/><br/>
State/Country:<span class="imp">*</span><br/>
<input type="text" name="country"
placeholder="State/Country" class="textbox" required /><br/>
<br/>
School/Institution:<span class="imp">*</span><br/><input
type="text" name="school" placeholder="Your School or
Institution" c lass="textbox" required /><br/><br/>
Your Profession:<br/><input type="text" name="profession"
placeholder="Profession" class="textbox"><br/><br/>
Phone:<br/><input type="text" name="phone" placeholder="Phn. no.
with country code" class="textbox" pattern="\+[0-9]+" title="Only
digits" /><br/><br/>
<input type="submit" value="Submit"
class="btn3"/>
<input type="reset" value="Clear" class="btn3" />
</form>
I have tried in postman API client to post the data but it shows 404 status while posting the data.
Do I have any problem in my code?
Any suggestion will be appreciated!
You are creating multiple app objects. One in app.js and a different one in routes/Inquiry.js.
For this type of application, you will want to be using only one app object that you share with anyone who needs it because only one of them is actually registered with your web server and is being used.
That is likely why your app.post() handler is not working because it's registered on the wrong app object - the one that is not connected to a live server.
There are several other things that look incorrect in the code:
The third argument to an app.post() callback is a next() function reference, not the err that you have declared and are testing.
You are doing require('./models/InquiryDetails'); and that module exports some things, but you are not assigning the result of the require to anything so those exports are unusable.
You are initializing the connect module, but if you are using a recent version of Express that module is no longer needed as the functionality it used to provide for Express is now built into Express.

Resources