I'm new to the MERN stack and backend programming. I have a React form that I want to submit to an MLab database using Express. I am unable to successfully POST the form data to the database. I'm not sure if I'm taking the correct approach or not. My form is working and I am able to log the fields, I run into problems when trying to do a POST request to submit the form data.
Here is my form:
import React from 'react';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Form, FormGroup, Label, Input } from 'reactstrap';
class AddBookModal extends React.Component {
constructor(props) {
super(props);
this.state = {
modal: false,
bookTitle: '',
author: '',
genre: ''
};
this.toggle = this.toggle.bind(this);
this.onSubmit = this.handleSubmit.bind(this);
}
toggle() {
this.setState(prevState => ({
modal: !prevState.modal
}));
}
handleSubmit = (event) => {
event.preventDefault();
const data = this.state;
//console.log("Data from form :" + data.bookTitle, data.author, data.genre);
}
handleInputChange = (event) => {
event.preventDefault();
this.setState({
[event.target.name]:
event.target.value
});
}
render() {
const {bookTitle} = this.state;
const {author} = this.state;
const {genre} = this.state;
return (
<div>
<Button id="add-book-button" onClick={this.toggle}>Add Book</Button>
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<ModalHeader toggle={this.toggle}>Add Book</ModalHeader>
<ModalBody>
<Form method="POST" action="/profile" id="add-book-form" onSubmit={this.handleSubmit} >
<FormGroup>
<Label for="book-title-label">Book Title</Label>
<Input
value={bookTitle}
name="bookTitle"
onChange={this.handleInputChange}
placeholder="Enter name of book" />
</FormGroup>
<FormGroup>
<Label for="book-author-label">Author</Label>
<Input
value={author}
name="author"
onChange={this.handleInputChange}
placeholder="Enter author of book" />
</FormGroup>
<FormGroup>
<Label for="exampleSelect">Genre</Label>
<Input
onChange={this.handleInputChange}
value={genre}
type="select"
name="genre"
id="exampleSelect">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Input>
</FormGroup>
<ModalFooter>
<Button color="primary" type="submit" onClick={this.toggle}>Submit</Button>{' '}
<Button color="secondary" onClick={this.toggle}>Cancel</Button>
</ModalFooter>
</Form>
</ModalBody>
</Modal>
</div>
);
}
}
export default AddBookModal;
Here is my Express route:
const router = require('express').Router();
const bodyParser = require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: false });
// localhost:3000/profile
// User Model
const User = require('../../models/user-model');
const Book = require('../../models/book-model');
// Checks if user is not logged in
const authCheck = (req, res, next) => {
if(!req.user) {
// If user is not logged in, redirect them to login page
res.redirect('/auth/login');
}
else {
// If user is logged in call next in router.get
next();
}
};
router.get('/', authCheck, (req, res) => {
res.send('you are logged in, this is your profile : ' + req.user);
});
router.post('/', urlencodedParser, (req, res) => {
console.log(req.body);
const newUser = new User({
name: req.body.name,
username: req.body.username,
githubID: req.body.githubID,
profileUrl: req.body.profileUrl,
avatar: req.body.avatar,
books: {
bookTitle: req.body.bookTitle,
author: req.body.author,
genre: req.body.genre
}
});
newUser.save()
.then(data => {
res.json(data)
})
.catch(err => {
res.send("Error posting to DB")
});
});
module.exports = router;
Here is my Express server:
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const authRoutes = require('./routes/api/auth');
const passportSetup = require('./config/passport-setup');
const cookieSession = require('cookie-session');
const keys = require('./config/keys');
const passport = require('passport');
const profileRoutes = require('./routes/api/profile-routes');
const bookRoutes = require('./routes/api/book-routes');
// Hooks up routes/api/items file
const items = require('./routes/api/items');
const app = express();
// Boderparser Middleware
app.use(bodyParser.json());
// sets up session cookies
app.use(cookieSession({
// Max age set to 1 day
maxAge: 24 * 60 * 60 * 1000,
// Uses cookieKey from keys file to encrypt
keys: [keys.session.cookieKey]
}));
// initialize passport
app.use(passport.initialize());
app.use(passport.session());
// DB Config
const db = require('./config/keys').mongoURI;
// Connect to mongodb
mongoose
.connect(db, { useNewUrlParser: true })
.then(() => console.log('MongoDB Connected'))
.catch(err => console.log(err));
// Use Routes, sets up routes/api/items to be used
app.use('/api/items', items);
app.use('/book', bookRoutes);
// Use auth.js's routes
app.use('/auth', authRoutes);
// Use profile-routes routes for profile page
app.use('/profile', profileRoutes);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`))
I'm unable to make a POST request and not sure why. The Express route handles some OAuth stuff and displays the logged in user's profile. On that same page I have the form which allows the user to add data and submit it. Is the logic from authCheck interfering with POSTing the form data? The router.post in my Express route does not successfully console.log anything.
I've seen people use Axios in the React form component itself to do a POST request. Is it better to do form POST requests with Axios or should I be handling it in the Express route?
You should use the axios for making the api call.
In your handleSubmit function you are using "event.preventDefault()" which will prevent the default behavior of the form.
Related
I am trying to send data input in my registration page to my mongoDB database through an express.js route but I keep getting a 404: not found error even though I have created this specific route.
server2.js (Setting up server, connecting to mongoDB, and create schema)
const express = require('express');
const app = express();
const cors = require("cors");
app.use(cors());
require("dotenv").config({path: "./config.env"});
const mongoose = require('mongoose');
//URI key
const mongoURI = process.env.ATLAS_URI;
console.log(mongoURI);
// Connect to the database
mongoose.connect(mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(console.log("Connected to MongoDb"))
.catch(err => console.log(err));
// Define a schema for the data in the "accounts" collection
const accountSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
min: 2,
max: 24,
},
email: {
type: String,
required: true,
unique: true,
max: 200,
lowercase: true,
},
password: {
type: String,
required: true,
min: 8,
max: 30,}
});
//we're going to validate the json
// Define a model for the "accounts" collection using the schema
const Account = mongoose.model('Account', accountSchema);
// Example route that finds all accounts in the collection and sends them as JSON
// Start the server
const server = app.listen(4000, () => {
console.log('Server started on port 4000');
});
server.on("listening", () => {
console.log("Server is listening");
});
server.on("error", () => {
console.error("Error starting server", error.message);
});
module.exports = Account;
records2.js (creating routes)
const express = require("express");
import Account from "../server2";
// recordRoutes is an instance of the express router.
// We use it to define our routes.
// The router will be added as a middleware and will take control of requests starting with path /record.
const accountsRoutes = express.Router();
import Account from "../server2";
import accountModel from "../server2";
// This route adds a new account
accountsRoutes.post("http://localhost:4000/accounts/add", async (req, res) => {
try {
const dePackage = JSON.parse(req);
console.log("Route has started");
// Checks if there's an account with the same username or email address
const existingAccount = await accountModel.findOne({
$or: [{email:dePackage.email}, {username: dePackage.username}]
});
if (existingAccount) {
return res.status(400).json({message: "An account with that email/username already exists"});
}
const newAccount = new Account({
email: dePackage.email,
username: dePackage.username,
password: dePackage.password}
);
newAccount.save();
//find all users
const allUsers = await User.find();
console.log(allUsers);
} catch (err) {
console.log(err);
return res.status(500).json({ message: "Internal server Error"})
}
})
registerPage.js (frontend that fetches route on submit)
import React, {useState} from "react";
function RegisterPage() {
// create states to be managed
const [email, setEmail] = useState("");
const [userName, setUserName] = useState("");
const [password, setPassword] = useState("");
// create routes to handle changes in form
const handleEmail = (event) => {
setEmail(event.target.value);
}
const handleUsername = (event) => {
setUserName(event.target.value);
}
const handlePassword = (event) => {
setPassword(event.target.value);
}
const handleSubmit = async (event) => {
event.preventDefault();
const response = await fetch("http://localhost:4000/accounts/add", {
method: "POST",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({email, userName, password}),
});
console.log(response.body);
const result = await response.json();
console.log(result);
}
return (
<div>
<form onSubmit={handleSubmit}>
<label htmlFor="email">email:</label>
<input name="email" id="email" type="text" onChange={handleEmail}/>
<label htmlFor="username">username: </label>
<input name="username" id="password" type="text" onChange={handleUsername} />
<br/>
<label htmlFor="password">password: </label>
<input name="password" id="password" type="text" onChange={handlePassword}/>
<br/>
<input type="submit" name="submit" value="register" />
</form>
</div>
)
}
export default RegisterPage;
I have tried to see if the server accepts the route, which it clearly didn't and I tried to see if there was a problem with json but it didn't seem to be that. I'm new with backend so help is greatly appreciated :).
There's a problem with your Express POST route.
Change this from:
accountsRoutes.post("http://localhost:4000/accounts/add", async (req, res) => {});
To this:
accountsRoutes.post("/accounts/add", async (req, res) => {});
Additionally, on the frontend you probably should proxy your React requests to Express in development. Otherwise, React is not aware of Express and is trying to fetch the frontend. Plus your current frontend fetch won't work on a staging or production environment.
For example, run npm i --save-dev http-proxy-middleware, then inside your React app's src folder create:
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
const proxy_urls = ['/api'];
const target = 'http://localhost:4000';
module.exports = function (app) {
proxy_urls.forEach((url) => {
app.use(url, createProxyMiddleware({ target }));
});
};
This reserves the /api suffix for React and funnels all of those /api calls to the backend or better known as a proxy. This file is telling React "all requests to /api should go to http://localhost:4000 and not http://localhost:3000.
Once that's in place your frontend component fetch can be modified:
const response = await fetch("/accounts/add", {});
I can't connect to my Express server. I'm trying to use Axios to connect from my front end but it never reaches the endpoint. Also when I attempt to connect directly from the browser it just spins. When I start the server, I do see the console log message from the listen function, so it appears to be running. But the Axios.post never gets tot he /login route.
Here is my index.js on the Server
const express = require('express');
const app = express();
const mysql = require('mysql');
const cors = require('cors');
app.use(cors);
app.use(express.json());
const db = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'root',
databaseName: 'ncaa'
});
app.post("/login", (req, res) => {
const userName = req.body.userName;
const password = req.body.password;
db.query("SELECT * from user where username = ? and password = ?", [userName, password], (err, result) => {
if(err){
res.send({err: err});
}
if (result){
res.send(result);
}
else {
res.send({message: "Username or password is incorrect"});
}
});
});
app.listen(3001, () => {
console.log('App running on 3001');
});
Here is my app.js on the client
import React from 'react';
import { useState } from 'react';
import Axios from 'axios';
import './App.css';
function App() {
const [userName, setUserName] = useState("");
const [password, setPassword] = useState("");
const authenticateLogin = () => {
Axios.post("http://localhost:3001/login", {
userName: userName,
password: password
}).then((response) => {
console.log(response);
});
}
return (
<div className="App">
<div className="login">
<h1>Login</h1>
<label>Username:</label>
<input type="text" onChange={(event) => { setUserName(event.target.value) }}/>
<label>Password:</label>
<input type="password" onChange={(event) => { setPassword(event.target.value) }}/>
<button onClick = {authenticateLogin}>Login</button>
</div>
</div>
);
}
export default App;
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors()); // cors is a function, you have to call that function
app.use(express.json());
This is my express server
const express = require("express");
const app = express();
const mongoose = require("mongoose");
require("dotenv").config();
const cors = require("cors");
const userData = require("./db");
var PORT = process.env.PORT || 3001;
app.use(cors());
app.use(express.urlencoded({ extended: false }));
mongoose
.connect("mongodb://localhost", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("Connected to database!!");
})
.catch((e) => {
console.log("Error : " + err);
});
app.get("/", (req, res) => {
res.json({ server: "Running" });
});
app.get("/login", (req, res) => {
res.json({ user: "true" });
});
app.post("/signup", (req, res) => {
console.log(req);
res.redirect("/");
});
app.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}`);
});
and this is my React component
import React, { useState } from "react";
import "./Signup.css";
import axios from "axios";
import { useSelector } from "react-redux";
import { selectAuth } from "../../features/authSlice";
export default function Login() {
const isAuthenticated = useSelector(selectAuth);
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
const handleSignup = async (e) => {
e.preventDefault();
var bodyFormData = new FormData();
bodyFormData.append("username", user);
bodyFormData.append("password", pass);
const res = await axios({
method: "post",
url: "http://localhost:3001/signup",
data: bodyFormData,
headers: { "Content-Type": "multipart/form-data" },
});
};
return (
<div className="signup__container">
<h2>Signup</h2>
<form className="signup__form" autoComplete="off">
<div className="signup__imgContainer">
<img
src={require("../../assets/avatar-placeholder.jpg").default}
alt="Avatar"
className="signup__avatar"
/>
</div>
<div className="signup__inputContainer">
<label for="uname">
<b>Username</b>
</label>
<input
type="text"
placeholder="Enter Username"
name="uname"
onChange={(e) => setUser(e.target.value)}
required
/>
<label for="psw">
<b>Password</b>
</label>
<input
type="password"
placeholder="Enter Password"
name="psw"
required
onChange={(e) => setPass(e.target.value)}
/>
<button type="submit" onClick={handleSignup}>
Signup
</button>
<label>
<input type="checkbox" name="remember" /> Remember me
</label>
</div>
</form>
<div>{isAuthenticated.toString()}</div>
</div>
);
}
I am trying to post data using axios but I am unable to access this data in my backend. Am i doing it right, if yes then can anyone tell me what's wrong. and if no then how can I correct it to use the data.
I tried various things like sending the data as params but nothing worked on server it was always undefined however the console.log(req) gives something but I didn't see any of my post data in it.
add app.use(express.json());
if it does not work. then add body-parser in your package.
const express = require("express");
const bodyParser = require("body-parser"); // install body-parser
const cors = require("cors");
const app = express();
app.use(bodyParser.json({ limit: "50mb" }));
app.use(
bodyParser.urlencoded({
limit: "50mb",
extended: true,
parameterLimit: 50000,
})
);
app.use(express.json()); /// add
In the React app, you're using Content-Type multipart/form-data. When you use this Content-Type, the server needs to parse the request differently. This Content-Type is used when you want to upload a file in the request. It isn't the case here, so you should use Content-Type application/json instead.
Your request should be:
const res = await axios({
method: "post",
url: "http://localhost:3001/signup",
data: { username : user, password : pass},
headers: { "Content-Type": "application/json },
});
And in the Express application, you need to add a middleware to parse incoming requests in application/json Content-Type.
app.use(cors());
app.use(express.urlencoded({ extended: false }));
app.use(express.json()); // <---- add this
In the route, you can see the data inside req.body
app.post("/signup", (req, res) => {
console.log(req.body);
res.redirect("/");
});
So I am using a nodejs server to post contact form information from my react app into nodemailer so that I can send emails to my gmail account (this works fine and the emails come through) My question is I want to get React to respond to the response from the post request. (IE change state and then compile a message depending on that state). However when I send an email the page redirects to show just the plain json message sent from node. How can I stop this from happening? (Beginner here so please go easy!)
FORM FILE:
import React from 'react';`enter code here`
import './form.scss';
import axios from 'axios'
class Form extends React.Component{
constructor(){
super();
this.state= {
email: "",
topic: "",
query: "",
result: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = (event)=> {
const {email, topic, query, error, success} = this.state;
const data ={
email,
topic,
query
};
event.preventDefault();
axios
.post('http://localhost:5000/', data)
.then((res) => {
this.setState({result: "success"})
})
.catch(err => {
console.error(err)
this.setState({result: "fail"})
})
}
handleChange = (event) => this.setState({
[event.target.name]: event.target.value
});
render(){
const {result, topic, email, query} = this.state;
if(result === "success"){
return(<div>YES</div>);
}
if(result === "fail"){
return(<div>NO</div>);
}
else{
return(
<div>
<form action="/" method="POST" className="contact-form" >
<label className="contact-label">Email
<input type="text" name="email" value={email} onChange={this.handleChange} />
</label>
<label className="contact-label">Topic
<input type="text" name="topic" value={topic} onChange={this.handleChange}/>
</label>
<label className="contact-label">Query
<textarea name="query" value={query} onChange={this.handleChange} id="" cols="30" rows="10"></textarea>
</label>
<button type="submit" className="submit-button" onSubmit={this.handleSubmit}>Send Email</button>
</form>
</div>
)
}
}
}
export default Form;
NODEJS FILE:
const express = require('express')
const path = require('path');
const PORT = process.env.PORT || 5000;
const { body,validationResult } = require('express-validator');
const nodemailer = require("nodemailer");
const bodyParser = require('body-parser');
const app = express();
const cors = require('cors');
const { google } = require("googleapis");
const { reseller } = require('googleapis/build/src/apis/reseller');
const OAuth2 = google.auth.OAuth2;
require('dotenv').config()
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(express.urlencoded({
extended: true
}))
if (process.env.NODE_ENV === 'production'){
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', function(req,res){
res.sendFile(path.join(__dirname, 'client/build' , 'index.html'))
})
}
app.use(
cors({
origin: 'http://localhost:3000',
credentials: true,
})
);
app.route('/')
.post([body('email').isEmail()],(req,res) => {
const errors = validationResult(req);
const email = req.body.email
const topic = req.body.topic
const query = req.body.query
const body = `${email} sent a message with the topic: ${topic} and content: ${query} `
const myOAuth2Client = new OAuth2(
process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
"https://developers.google.com/oauthplayground"
)
myOAuth2Client.setCredentials({
refresh_token: process.env.REFRESH_TOKEN
});
const myAccessToken = myOAuth2Client.getAccessToken()
const transport = nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
user: process.env.SECRET_EMAIL, //your gmail account you used to set the project up in google cloud console"
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
refreshToken: process.env.REFRESH_TOKEN,
accessToken: myAccessToken //access token variable we defined earlier
}});
const mailOptions = {
from: email,
to: process.env.SECRET_EMAIL,
subject: topic,
text: body
};
if (!errors.isEmpty()) {
console.log(error)
} else {
transport.sendMail(mailOptions, function(error, info){
if(error){
res.status(400).send({message: "failed"})
}else{
res.status(200).send({message: "success"})
};
});
}
});
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}!`)
});
So I have a MERN application, and everything works fine. Post, Get, and Delete all work fine but on my React terminal i keep getting an error saying
Proxy error: Could not proxy request /user/update from localhost:3000 to
http://localhost:5000.
See https://nodejs.org/api/errors.html#errors_common_system_errors for more information.
(ECONNRESET).
I'm just lost on how my application can work fine, like it adds, deletes, and gets data from my mongoDB server just fine, but this message keeps popping up. I've made some changes to my route in order to try getting it working on heroku, which hasn't been successful as well. However locally, everything seems to work besides that error message I keep getting.
Inside my package.json I have specified
"proxy": "http://localhost:5000/"
My server.js is
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const bodyParser = require("body-parser");
const path = require("path");
// Allow to get information from .env file
require("dotenv").config();
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const url = process.env.ATLAS_URL;
// Express.static --> charge of sending static files requests to the client
app.use(express.static(path.join(__dirname, "client", "public")));
mongoose.connect(url, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false,
});
// Checking to see if mongoose connection was successful
const connection = mongoose.connection;
connection.once("open", () => {
console.log("MongoDB database connection established successfully");
});
// Routes
const userRouter = require("./routes/user");
app.use("/user", userRouter);
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/public"));
}
// "Catchall" route hander.
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "public", "index.html"));
});
app.listen(port, () => {
console.log("Server is running on port: " + port);
});
My routes is this
const router = require("express").Router();
const User = require("../models/user.model");
// Gets users from database
router.route("/").get((req, res) => {
User.find()
.then((user) => res.json(user))
.catch((err) => res.status(400).json("Error: " + err));
});
// Adds a new user to the userDB
router.route("/signup").post((req, res) => {
const username = req.body.username;
const password = req.body.password;
const email = req.body.email;
const newUser = new User({
username,
password,
email,
});
newUser
.save()
.then(() => res.json("User added to userDB"))
.catch((err) => res.status(400).json("Error: " + err));
});
// Adds a new todoList for the current user
router.route("/update").post((req, res) => {
const userId = req.body.id;
const newTodoList = {
title: req.body.title,
content: req.body.content,
};
// Finds user and updates the todoList for that user
const filter = { _id: userId };
const update = { $push: { todoList: newTodoList } };
User.findOneAndUpdate(filter, update)
.then(() => res.json("User updated with todoList"))
.catch((err) => res.status(400).json("Error: " + err));
});
// Gets userId and noteId from parameters and find the current user and deletes the note that wants to be deleted.
router.route("/delete/:userId/:noteId").delete((req, res) => {
const userId = req.params.userId;
const noteId = req.params.noteId;
const filter = { _id: userId };
const update = { $pull: { todoList: { _id: noteId } } };
User.findOneAndUpdate(filter, update)
.then(() => res.json("Note Deleted"))
.catch((err) => res.status(400).json("Error: " + err));
});
module.exports = router;
My React client side code looks like this
import React, { useState } from "react";
import axios from "axios";
import AddIcon from "#material-ui/icons/Add";
function CreateArea(props) {
const [isExpanded, setIsExpanded] = useState(false);
const [note, setNote] = useState({
id: "",
title: "",
content: "",
});
// Updating hooks when user types
function handleChange(e) {
const { name, value } = e.target;
setNote((prevNote) => {
return {
...prevNote,
[name]: value,
id: props.id,
};
});
}
// Posting note to db
function submitNote(e) {
// Clears user's title and content after user clicks submit button
setNote({
id: "",
title: "",
content: "",
});
// Posts user with new todoList
axios.post("/user/update", note).then((res) => console.log(res.data));
}
function expand() {
setIsExpanded(true);
}
return (
<div>
<form className="createArea">
{isExpanded ? (
<input
name="title"
onChange={handleChange}
value={note.title}
placeholder="Title"
/>
) : null}
<textarea
name="content"
onChange={handleChange}
value={note.content}
placeholder="Take a note..."
rows="3"
onClick={expand}
/>
{isExpanded ? (
<button onClick={submitNote}>
<AddIcon />
</button>
) : null}
</form>
</div>
);
}
export default CreateArea;