How to post data from react to express - node.js

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"})
})

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

I am sending form data which includes files as well, however while accessing req.files in express it returning undefined

I am sending formdata in my react app which includes file also. However when I try to access req.files in my express server it always return "Undefined". (I have gone through similar questions but they are not resolving my problem)
Here is my Client side code
const AddCategory = () => {
const categoryName = useRef("");
const categoryImage = useRef("");
async function PostCategory(e) {
e.preventDefault();
//checking file type and size
categoryImage.current.files[0].type.slice(0, 5) != "image" ? alert("Only Images are acceptable") :
categoryImage.current.files[0].size > 1024 * 1024 ? alert("Oversized Image File") :
console.log("Continue");
let data = new FormData();
data.append('name', JSON.stringify(categoryName.current.value));
data.append('file', categoryImage.current.files[0]);
let response = await fetch("AddCategory", {
method: 'POST',
body: data,
header: {
'Content-Type': 'multipart/form-data',
}
})
}
return (
<div>
<NavigationBar />
<div className="jumbotron">
<h3>Add Category</h3>
</div>
<div className="container">
<form onSubmit={(e) => PostCategory(e)} id="AddCategory" className="text-alignment-left">
<Row className="mb-3">
<Form.Group as={Col} className="mb-3" controlId="formGridcategoryName">
<Form.Label>Category</Form.Label>
<Form.Control ref={categoryName} type="text" required />
</Form.Group>
<Form.Group as={Col}>
<Form.Label>Image</Form.Label>
<Form.Control ref={categoryImage} name="categoryImage" type="file" accept="image/*" required />
</Form.Group>
</Row>
<Button type="submit" variant="primary">
Add Category
</Button>
</form>
</div>
</div>
)
}
Here is my server side code
const express = require('express');
const bodyParser = require('body-parser');
const routesHandler = require('./routes/handlers.js');
const fileUpload = require('express-fileupload');
const app = express();
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
app.use('/', routesHandler);
app.use(fileUpload())
const PORT = 4000;
app.listen(PORT, () => {
console.log(PORT);
})
router.post("/AddCategory", (req, res) => {
console.log("Request Received");
console.log(req.files.file);
});
May anybody suggest what is going wrong?

MEAN stack issue with posting form data

Evening All,
I'm trying to build a simple register page with mongodb, node, express, mongoose and angularJS.
I'm struggling to understand the relationship between all of the components and how to wire them all together.
This is my form...
<div class="main">
<p class="sign" align="center">Register</p>
<form class="form1">
<input class="un " type="email" align="center" placeholder="Email" name="username" required>
<input class="pass" type="password" align="center" placeholder="Password" name="password" required>
<button type="submit" class="btn btn-danger" align="center" ng-click="submit()">Register</button>
</div>
My controller for that view...
angular.module('registerCtrl', [])
.controller('registerController', function ($scope, $http, dataService) {
$scope.submit= function(){
console.log('clicked submit');
$http.post('/RegisterUser').then(function(response){
console.log(response.data);
console.log(response.status)
})
}
});
server.js for server and db logic...
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const app = express();
const router = express.Router();
mongoose.connect("mongodb://localhost:27017/userDB", { useNewUrlParser: true });
const userSchema = new mongoose.Schema({
email: String,
password: String
});
const User = new mongoose.model("User", userSchema);
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(express.static('node_modules'));
app.use(express.static('public'));
const port = 3000;
app.listen(port);
console.log('Server is running on port 3000');
//POSTS
app.post("/RegisterUser", function (req, res) {
const newUser = new User({
email: req.body.username,
password: req.body.password
});
newUser.save();
})
view routes...
angular.module('app.routes', [])
.config(function ($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "views/Login.html"
})
.when("/Register", {
templateUrl: "views/Register.html",
controller: "registerController"
})
.when("/Home", {
templateUrl: "views/Home.html",
controller: "homeController"
})
.when("/CocktailDetails", {
templateUrl: "views/CocktailDetails.html",
controller: "cocktailDetailsController"
})
.when("/Favourites", {
templateUrl: "views/Favourites.html",
controller: "favouritesController"
})
.otherwise({
redirectTo: "/"
})
})
Essentially what I want to achieve is simply posting the inputted email and password to the database and then, if the POST was successful divert the view route to my home view.
Should I be passing in the data from the form into the post request in my controller as opposed to the server side? As currently the post data is being passed as null.
Is someone able to explain the best way to achieve this and describe the relationship between the database, server and client side framework in this scenario.
Thanks
Client
It looks like your form is missing ng-models and you're also not posting their data. Add ng-model to the inputs:
<input type="email" ng-model="form.username">
<input type="password" ng-model="form.password">
Also create their bindings in the controller. Then pass the form data along to the server when you post:
.controller('registerController', function ($scope, $http, $location, dataService) {
// injecting $location now ^
$scope.form = { // <-- An object whose properties are bound to the form inputs
username: '',
password: ''
}
$scope.submit = function(){
console.log('clicked submit');
$http.post('/RegisterUser', $scope.form) // <-- posting `$scope.form` data now
.then(function(response){
console.log(response.data);
console.log(response.status);
$location.path('/Home'); // <-- redirecting to Home route
})
}
});
Server
Since there was no data being posted before, req.body was empty on the server. The server is also not sending back a response. If you wanted to send back the user object, you could add this to the server route after newUser.save():
return res.status(200).send(newUser);
send is what sends a response, return just exits the function.

Sending email to several recipients via nodemailer doesn't work

I tried looking for different answers but they didn't work. I don't receive the email in my gmail account when I am trying to send it, I am just getting it in my Ethereal account.
This is my server code:
const express = require('express');
const path = require('path');
const cons = require('consolidate');
const bodyParser = require('body-parser');
const nodemailer = require("nodemailer");
const app = express();
app.engine('html', cons.swig)
app.set('../public', path.join(__dirname, 'public'));
app.use('../src/css', express.static(path.join(__dirname, 'src/css')));
app.use('../src/js', express.static(path.join(__dirname, 'src/js')));
app.use(bodyParser.urlencoded({ extended: false}));
app.use(bodyParser.json());
app.post('/send',(req,res) => {
const output = `
<p>name of client: ${req.body.name}</p>
`;
let transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
secure: false,
auth: {
user: "*****#ethereal.email",
pass: "********"
},
tls: {
rejectUnauthorized: false
}
});
let mailOptions = {
from: `${req.body.email}`,
to: "******#gmail.com",
subject: "Node contact request",
text: "Hello world?",
html: output
};
transporter.sendMail(mailOptions, (error, info) => {
if(error) {
return console.log(error);
}
console.log("Message sent: %s", info.messageId);
console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
res.render('index')
});
});
app.listen(5000);
I am using react app for my front-end(e-commerce), and I want that after sending the email the client will see a message saying "The email has been sent" (the location doesn't really matter for now). How can I do it after submitting the form? Right now the client is directing to localhost:5000/send instead of staying at the same page.
This is my react code:
import React from 'react';
const contact = () => {
return (
<form className="contact" action="http://localhost:5000/send" method="post">
<div className="contact-topFields">
<div className="contact-topFields-email">
<input
className="contact-topFields-email-input"
type="email"
name="email"
required/>
<ion-icon id="email-icon" name="mail"></ion-icon>
<p className="contact-topFields-email-text">Email</p>
</div>
<div className="contact-topFields-name">
<input
className="contact-topFields-name-input"
type="text"
name="name"
required/>
<ion-icon id="name-icon" name="person"></ion-icon>
<p className="contact-topFields-name-text">Name</p>
</div>
</div>
<div className="contact-bottomFields">
<div className="contact-bottomFields-phone">
<input
className="contact-bottomFields-phone-input"
type="text"
name="phonenumber"
required/>
<ion-icon id="phone-icon" name="call"></ion-icon>
<p className="contact-topFields-phone-text">phone</p>
</div>
</div>
<div className="contact-text">
<textarea className="contact-text-textarea" name="message" required></textarea>
</div>
<button className="contact-submitButton" type="submit">send </button>
</form>
)
}
export default contact;
To prevent the page being directed to localhost:5000/send:
In order to trigger sending the email while keep the user's view unchanged, you may want to override the form's submit function.
For example, you may want to do this in your JSX code:
<form className="contact" onSubmit={{this.handleSubmit}}>
<- whatever inside form ->
</form>
Then you may want to define a function handleSubmit:
function handleSubmit (evt) {
evt.preventDefault() // this is used to prevent ui from being directed
// Do http request here, use a http agent such as fetch(), axios() etc etc.
}

Post request saving only versionKey with Mongoose and Express

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"
}

Resources