How to send data from React to Express - node.js

I'm trying to create login/registration for an app using React/Node/Express/Postgres. Where I'm getting stuck is receiving data on the server side from my form in React.
I have a register component for the form in register.js
import React from 'react';
import useForm from '../form/useForm';
const Register = () => {
const { values, handleChange, handleSubmit } = useForm({
name: '',
email: '',
password: "",
password2: ""
}, register);
function register() {
console.log(values);
}
return (
<div className="row mt-5">
<div className="col-md-6 m-auto">
<div className="card card-body">
<h1 className="text-center mb-3">
<i className="fas fa-user-plus"></i> Register
</h1>
<form
action="/users/register"
method="POST"
onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="name">Name</label>
<input
className="form-control"
type="name"
name="name"
onChange={handleChange}
placeholder="Enter Name"
value={values.name}
required />
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<input
className="form-control"
type="email"
name="email"
onChange={handleChange}
placeholder="Enter Email"
value={values.email}
required />
</div>
<div className="form-group">
<label htmlFor="email">Password</label>
<input
className="form-control"
type="password"
name="password"
onChange={handleChange}
placeholder="Create Password"
value={values.password}
required />
</div>
<div className="form-group">
<label htmlFor="email">Confirm Password</label>
<input
className="form-control"
type="password"
name="password2"
onChange={handleChange}
placeholder="Confirm Password"
value={values.password2}
required />
</div>
<button type="submit" className="btn btn-primary btn-block">
Register
</button>
</form>
<p className="lead mt-4">Have An Account? Login</p>
</div>
</div>
</div>
);
};
export default Register;
A hook to handle the form actions in useForm.js
import {useState, useEffect} from 'react';
const useForm = (initialValues, callback) => {
const [hasError, setErrors] = useState(false);
const [values, setValues] = useState(initialValues);
const handleSubmit = (event) => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(setValues(values => ({ ...values, [event.target.name]: event.target.value })))
}
fetch("/users/register", options)
}
const handleChange = (event) => {
event.persist();
setValues(values => ({ ...values, [event.target.name]: event.target.value }));
};
return {
handleChange,
handleSubmit,
values,
}
};
export default useForm;
Then I have a file to manage the routes for logging in/registering in users.js
const express = require("express");
const Router = require("express-promise-router");
const db = require("../db");
const router = new Router();
//Login page
router.get('/login', (req, res) => res.send("Login"));
//Register page
router.get('/register', (req, res) => res.send("Register"));
//Register Handle
router.post('/register', (req, res) => {
console.log(req.body);
res.send('hecks');
});
module.exports = router;
I have tried messing with things inside of the handleSubmit function in my useForm.js hook, but everything leads to the console.log(req.body) from my users.js file to return as undefined. Where am I going wrong?
Edit #1: Snip from Postman sending post request
Edit #2: basic project structure
.
./client
./client/src
./client/src/components
./client/src/components/register
./client/src/components/register/register.js
./client/src/components/form
./client/src/components/form/useForm.js
./client/src/App.js
./routes
./routes/index.js
./routes/users.js
./server.js
Edit #3: Main server.js file
const express = require("express");
const mountRoutes = require("./routes");
const app = express();
mountRoutes(app);
var bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//catch all other routes
app.get("*", function(req, res) {
res.send("<h1>Page does not exist, sorry</h1>");
});
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));

You’re setting state in JSON.stringify which returns undefined. you’ve to pass values in it:
const handleSubmit = (event) => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(values)
}
fetch("/users/register", options)
}

You need to apply bodyParser before mounting routes.
So change like this:
var bodyParser = require("body-parser");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
mountRoutes(app);
You don't use then or await in the handleSubmit function which may cause problem.
Can you update the handleSubmit function like this and try?
const handleSubmit = async event => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(values)
};
try {
const response = await fetch("/users/register", options);
const responseData = await response.json();
if (response.ok) {
console.log("response ok");
callback();
} else {
console.log("response NOT ok");
throw new Error(responseData.message);
}
} catch (err) {
console.log(err);
if (err.response) {
console.log(err.response.data);
}
}
};

πŸ‘¨β€πŸ« You can try with this code below:
userForm.js: Make sure your handleSubmit in your userForm.js it's looks like this code below: πŸ‘‡
const handleSubmit = async(event) => {
if (event) event.preventDefault();
const options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(values)
}
try {
// change with your endpoint
const endpoint = 'http://localhost:3001/users/register';
const result = await fetch(endpoint, options);
// send value to your register function
callback(result);
} catch (ex) {
console.log('Something failed');
console.log(ex);
}
}
You've to use callback(result), so you can console.log that value on your register function.
express server: Make sure in your express server, you've been add body-parser, it's will looks like this code below: πŸ‘‡
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
That code above will make your req.body works.
I hope it can help you πŸ™.

Related

Getting an error: Admin.js:14 POST http://localhost:3009/api/upload 500 (Internal Server Error)

Why am i getting an Admin.js:14 POST http://localhost:3009/api/upload 500 (Internal Server Error)
this is the error that I'm getting when trying to submit my form on the front end to the backend
my port on the backend is set to 3009 and my endpoint when fetching the 3009 is localhost:3009/api/upload on the backend
title, genre, bpm, key, and duration
Here is the code to Admin.js
import React, { useState } from 'react'
const Admin = () => {
const [title, setTitle] = useState("");
const [genre, setGenre] = useState("");
const [bpm, setBpm] = useState("");
const [key, setKey] = useState("");
const [duration, setDuration] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
fetch("http://localhost:3009/api/upload", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title,
genre,
bpm,
key,
duration
})
})
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error);
});
}
return (
<div className="flex flex-col items-center justify-center w-[100%] h-[100vh]">
<p className="mb-[40px]">Upload Song</p>
<form onSubmit={handleSubmit} className="flex flex-col w-[350px] gap-[10px]">
<input type="text" placeholder="Title" onChange={(e) => setTitle(e.target.value)} />
<input type="text" placeholder="Genre" onChange={(e) => setGenre(e.target.vaue)} />
<input type="number" placeholder="BPM" onChange={(e) => setBpm(e.target.value)} />
<input type="text" placeholder="Key" onChange={(e) => setKey(e.target.value)} />
<input type="text" placeholder="Duration" onChange={(e) => setDuration(e.target.value)} />
<button className="bg-red-500" type="submit">Upload</button>
</form>
</div>
)
}
export default Admin
Here is the code to uploadSong.js
const express = require("express");
const router = express.Router();
const UploadSongSchema = require("../models/uploadSong");
router.post("/upload", async (req, res) => {
const {
title,
genre,
bpm,
key,
duration,
} = req.body;
const song = new UploadSongSchema({
title,
genre,
bpm,
key,
duration,
});
try {
await song.save();
res.status(201).send(song);
} catch (error) {
console.error(error);
res.status(500).send("Server error");
}
})
module.exports = router;
Here is the code to index.js
const mongoose = require("mongoose");
const express = require("express");
const dotenv = require("dotenv")
const uploadRoute = require("./routes/uploadSong");
const app = express();
const cors = require("cors");
const port = 3009;
dotenv.config();
app.use(express.json());
app.use(cors());
mongoose.connect(process.env.MONGO_URL, {
useNewUrlParser: true.value,
useUnifiedTopology: true,
}).then(() => {
console.log("Connected to DB")
})
app.use("/api", uploadRoute);
app.listen(3009, () => {
console.log(`Listening on port ${port}`)
})
i have tried refractor my kind and everything and keep getting different errors
The issue is you stringified the body on the client but on the server you didn't parse it.
Before destructing, make sure you parse the string somehow:
const {
title,
genre,
bpm,
key,
duration,
} = JSON.parse(req.body);

Cookie error in node.JS app with next.js client program

I am actually write a node.JS application on natours project. It is actually a course project that was designed by jonas. I am designing a next.JS client side program of this backend program. Problem was that when I submit a request on http://localhost:3000/api/v1/users/signup url everything is ok, cookie is set but when I send a request on others url on this server like http://localhost:3000/api/v1/tours cookie is not read by the server and req.cookies is null. I am trying to solve this problem but I am stuck.
app.js Code
const express = require("express");
const morgan = require("morgan");
const bodyParser = require("body-parser");
const rateLimit = require("express-rate-limit");
const helmet = require("helmet");
const xss = require("xss-clean");
const mongoSanitize = require("express-mongo-sanitize");
const hpp = require("hpp");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const globalErrorHandler = require("./controllers/errorController");
const tourRouter = require("./routes/tourRoutes");
const userRouter = require("./routes/userRoutes");
const reviewRouter = require("./routes/reviewRoutes");
const AppError = require("./utils/appError");
const app = express();
app.set("view engine", "pug");
app.set("views", `${__dirname}/views`);
// GLOBAL MIDDLEWARE
if (process.env.NODE_ENV === "development") {
app.use(morgan("dev"));
}
const limiter = rateLimit({
max: 100,
windowMs: 60 * 60 * 1000,
message: "Too many requests from this IP, Please try again in an hour.",
});
app.use(cors({ credentials: true, origin: "http://localhost:3001" }));
app.use(helmet());
app.use(xss());
app.use("/api", limiter);
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json({ limit: "10kb" }));
app.use(express.static(`${__dirname}/public`));
app.use(mongoSanitize());
app.use(
hpp({
whitelist: [
"duration",
"ratingsQuantiry",
"ratingsAverage",
"difficulty",
"maxGroupSize",
"price",
],
})
);
app.use((req, res, next) => {
req.requestTime = Date.now();
console.log(req.cookies.jwt);
next();
});
app.get("/", (req, res) => {
res.status(200).render("base", {
title: "Natours",
});
});
app.use("/api/v1/tours", tourRouter);
app.use("/api/v1/users", userRouter);
app.use("/api/v1/reviews", reviewRouter);
app.all("*", (req, res, next) => {
// res.status(404).json({
// status: "fail",
// message: `Can't find ${req.originalUrl} on ther server.`,
// });
const err = new AppError(
`Can't find ${req.originalUrl} on ther server.`,
404
);
next(err);
});
app.use(globalErrorHandler);
module.exports = app;
Client Code in Next.js
import React, { useState } from "react";
import axios from "axios";
import Layout from "../components/Layout";
const url = "http://localhost:3000/api/v1/users/signup";
const styles = {
"input-group": "flex flex-col gap-3",
"input-label": "px-2 text-md font-thin",
"text-input": "px-2 py-4",
button:
"px-4 py-3 font-thin uppercase text-md bg-teal-700 hover:bg-teal-600 text-slate-50 rounded-sm",
"cancle-button": "bg-red-700 hover:bg-red-600",
};
function Register() {
const [formData, setFormData] = useState({
name: "",
email: "",
password: "",
passwordConfirm: "",
photo: "",
});
const handleChange = (event) => {
setFormData({
...formData,
[event.target.name]: event.target.value,
});
};
const handleSubmit = async (event) => {
event.preventDefault();
axios
.post("http://localhost:3000/api/v1/users/signup", formData, {
withCredentials: true,
})
.then((response) => console.log(response))
.catch((err) => console.log(err));
alert(JSON.stringify(formData));
};
const handleReset = (event) => {
setFormData({
name: "",
email: "",
password: "",
passwordConfirm: "",
photo: "",
});
};
return (
<Layout>
<div className="p-10 bg-slate-50">
<form onSubmit={handleSubmit}>
<h1 className="text-2xl mb-5 text-teal-700">Register your account</h1>
<div className="w-1/2 flex flex-col gap-3">
<div className={styles["input-group"]}>
<label className={styles["input-label"]}>Name</label>
<input
className={styles["text-input"]}
placeholder="Enter your name"
type="text"
name="name"
value={formData.name}
onChange={handleChange}
required
/>
</div>
<div className={styles["input-group"]}>
<label className={styles["input-label"]}>Email</label>
<input
className={styles["text-input"]}
placeholder="Enter your email"
type="email"
name="email"
value={formData.email}
onChange={handleChange}
required
/>
</div>
<div className={styles["input-group"]}>
<label className={styles["input-label"]}>Password</label>
<input
className={styles["text-input"]}
placeholder="Enter your password"
type="password"
name="password"
value={formData.password}
onChange={handleChange}
required
/>
</div>
<div className={styles["input-group"]}>
<label className={styles["input-label"]}>Confirm Password</label>
<input
className={styles["text-input"]}
placeholder="Enter your confirm password"
type="password"
name="passwordConfirm"
value={formData.passwordConfirm}
onChange={handleChange}
required
/>
</div>
</div>
<div className="bg-slate-100 flex gap-3 mt-5 px-3 py-6 w-1/2">
<button className={styles.button} type="submit">
Register
</button>
<button
className={`${styles.button} ${styles["cancle-button"]}`}
type="reset"
onClick={handleReset}
>
Cancle
</button>
</div>
</form>
</div>
</Layout>
);
}
export default Register;

POST http://localhost:3000/api/users/login 404 (Not Found)

So I have a login form that currently looks like that :
import React, { useState, useEffect} from "react";
import { Form, Button, Row, Col } from "react-bootstrap";
import { Link } from "react-router-dom";
import "./LoginScreen.css";
import axios from 'axios';
function LoginScreen() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const submitHandler = async (e) => {
e.preventDefault();
try {
const config = {
headers: {
"Content-type": "application/json",
},
}
setLoading(true)
const { data } = await axios.post(
'/api/users/login',
{
email, password,
},
config);
console.log(data);
localStorage.setItem("userInfo", JSON.stringify(data));
setLoading(false);
} catch (error) {
setError(error.response.data.message);
}
}
return (
<div className="login_outer">
<h1>Login Here</h1>
<div className="loginContainer">
<Form onSubmit={ submitHandler }>
<Form.Group controlId="formBasicEmail" >
<Form.Label>E-mail: </Form.Label>
<Form.Control size="lg" type="email" value={email} placeholder="Enter Email" className="input" onChange={(e) => setEmail(e.target.value)}/>
</Form.Group>
<Form.Group controlId="formBasicPassword">
<Form.Label>Password: </Form.Label>
<Form.Control size="lg" type="password" value={password} placeholder="Enter Password" className="input" onChange={(e) => setPassword(e.target.value)}/>
</Form.Group>
<Button className="login_button" variant="primary" type="submit">
Submit
</Button>
</Form>
<Row className="py-3">
<Col>
New User ? <Link to="/register">Register Here</Link>
</Col>
</Row>
</div>
</div>
);
}
export default LoginScreen;
When I click the submit button I get the error:
POST http://localhost:3000/api/users/login 404 (Not Found)
In my server.js I have:
const userRoutes = require('./routes/userRoutes.js');
const dotenv = require('dotenv');
const express = require('express');
const connectDB = require('./config/db');
const { notFound, errorHandler } = require('./middlewares/errorMiddleware');
dotenv.config();
connectDB();
const app = express();
app.use(express.json());
app.get("/", (req, res) => {
res.send("API is running..");
});
app.use('/api/users', userRoutes);
app.use(notFound);
app.use(errorHandler);
const PORT = process.env.PORT || 5000;
app.listen(PORT, console.log(`Server started on PORT ${PORT}`));
and in my routes I have:
const express = require('express');
const { registerUser, authUser } = require('../controllers/userControllers');
const router = express.Router();
router.route('/').post(registerUser);
router.post("/login", authUser);
module.exports = router;
Any idea why this is happening and how to fix it ? I think it is something to do with the server file.
UPDATE!: I have fixed the error by getting a proxy, but now I when I try to login I get 401 unauthorized. Anyone know how to fix that?
Delete "proxy": "http://localhost:3000". Install package http-proxy-middleware with command:
npm install http-proxy-middleware --save
Create a file setupProxy.js inside your src folder or the root of your folder. Add these lines inside:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
})
);
};

Post Request between React and NodeJS (handling response)

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}!`)
});

how to take information from html input and pass into nodeJS variable

I have one question.
I have form in index.html page(see below).
<form action="/" method="post">
<input type="text" name="name" placeholder="customer name"/>
<button type="submit" name="submit">Submit</button>
</form>
and have also server.js page with code as you see in below
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.get("/", function(req, res) {
res.sendFile(__dirname + "/index.html");
});
app.get("/api/customers", (req, res) => {
var name = req.body.name;
const customers = [
{ id: 1, firstName:name}
];
res.json(customers);
});
const port = 5000;
app.listen(port, () => console.log(`server ${port}`));
Now my question is:
When I am writing something in my input I want to take that text and pass it into my nodeJS.
For example if I write "Test" and press "Submit" button it should show "[{"id":1,"firstName":"Test"}]
you can use the fetch to make a request and then get it's response,
handleInputChange = (e) => {
this.setState({
name : e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
const data = { name : this.state.name };
fetch('/api/customers', {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data) // body data type must match "Content-Type" header
})
.then((response) => response.json())
.then((result) => {
this.setState({ customers : result}, () => {
console.log('Success:', result);
});
})
.catch((error) => {
console.error('Error:', error);
});
}
and you can use the handleInputChange and handleSubmit in the form,
<form onSubmit={this.handleSubmit} id="form">
<input type="text" name="name" onChange={this.handleInputChange} placeholder="customer name"/>
<button type="submit" name="submit">Submit</button>
<div id="result">{this.state.customers && this.state.customers.map(cust => {
return (
<span key={cust.id}>{cust.name}</span>
)
})}</div>
</form>

Resources