I've followed several tutorials and SO posts, but I'm having issues sending data from a React form to the backend. I've tried both fetch and axios and nothing seems to be working. I've checked the network tab and can see that the request has been sent to the correct URL and it has returned a 200 code - but nothing is being logged in the back end.
App.js
// Require modules
const express = require("express");
const mongoose = require("mongoose");
const session = require("express-session");
var cors = require('cors');
const app = express();
app.use(cors());
// Set up
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
const port = 5000;
// Mongoose
mongoose.connect("mongodb+srv://georgegilliland:94AJK6OlK5vasVOn#george-cluster-jjfzz.mongodb.net/DPFootball?retryWrites=true&w=majority", {useNewUrlParser: true}, ()=>{
console.log("DB connected")
});
// Controllers
let login = require('./controllers/login');
app.use('/api/login', function (req, res) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader('Access-Control-Allow-Methods', '*');
res.setHeader("Access-Control-Allow-Headers", "*");
res.end();
});
// POST /login
app.post("/api/login", function(req, res) {
console.log(req.body)
});
app.listen(port, () => {
console.log("Server is on, baby")
})
login.js
import React, { Component } from 'react';
import axios from 'axios';
import './login.css';
class Login extends Component{
constructor(props) {
super(props)
this.state = {
email: "",
password: ""
}
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
handleSubmit = e => {
e.preventDefault();
const { email, password } = this.state;
const user = {
email,
password
};
axios
.post('http://localhost:5000/api/login', user)
.then(() => console.log('done'))
.catch(err => {
console.error(err);
});
};
render(){
return (
<div id="login">
<div className="background-inner-container">
{/* <p>{this.state.response}</p> --> */}
<div className="login-register-container padding-top padding-bottom padding-left padding-right">
<div className="login-register-title">Account Login</div>
<form onSubmit={this.handleSubmit}>
<input className="form-input" type="email" id="email" name="email" placeholder="Enter Email" onChange={this.onChange}/>
<input className="form-input" type="password" id="password" name="password" placeholder="Enter Password" onChange={this.onChange}/>
<button className="form-button" type="submit">Login</button>
</form>
</div>
</div>
</div>
);
}
}
export default Login
The problem is the Express middleware. An Express middleware takes three parameters: (req, res, next). Currently you are omitting the third parameter next, which is needed to forward the request to the following handlers. Also you are currently ending the response with res.end() before the POST handler function is reached.
Try this:
app.use('/api/login', function (req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader('Access-Control-Allow-Methods', '*');
res.setHeader("Access-Control-Allow-Headers", "*");
next();
});
I've removed the res.end() and called the next() function instead in order to proceed with the request.
EDIT
By the way, if you only want to set CORS header, there's a handy and very common Express middleware called cors, which is highly customizable and will fit your needs. You can use it like this:
const cors = require('cors')
// ...
app.use(cors())
Related
I did find a solution approach to this for express servers, but I am not able to implement the same to my react-app.
referred to this. Please suggest. (I am beginner to CORS)
Here is my App.js file:
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const APP_ID = "--"
const APP_KEY = "--"
const [counter, setCounter] = useState(0);
useEffect(() => {
getRecipes();
}, [])
const getRecipes = async () => {
const response = await fetch(`https://api.edamam.com/search?q=chicken&app_id=${APP_ID}&app_key=${APP_KEY}&from=0&to=3&calories=591-722&health=alcohol-free`);
const data = response.json();
console.log(data);
}
return <div className="App">
<form className="search-form">
<input className="search-bar" type="text" placeholder="Search query here!" />
<button
className="search-button" type="submit">
Search
</button>
</form>
<h1 onClick={() => setCounter(counter + 1)}>{counter}</h1>
</div>;
}
export default App;
For production and almost all use cases, this needs to be done on a server (i.e the backend you are using, usually running node.js, not the frontend on react).
However, create-react-app does let you set a proxy on the client-side to test during development, albeit it is not recommended, since if you forget to remove it when pushing your code out to production, its can be a serious security issue. If its just a learning project you can do this:
If this is your API: http://123.4.345.53:7000/api/profile/
add this part in your package.json file: "proxy": "http://123.4.345.53:7000/"
Then you can do the following:
React.useEffect(() => {
axios
.get('api/profile/')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
});
If you decide to use a dedicated backend, and it runs node.js, this is done like so:
var app = express();
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
I'm new to mern so wondering if this is even possible or if there are other solutions(trying to validate if a user is a user exists or not which works, then redirect to the same page which doesn't work). I know about window.location in react but I don't know how to redirect if a user already exists from react - code is below:
react register js:
import React from 'react'
import { useState } from 'react'
import { Link } from 'react-router-dom'
import Axios from "axios";
const Register = () => {
const [regUsername, setRegUsername] = useState('')
const [regPassword, setRegPassword] = useState('')
const submitUser = () => {
// testing if connected to express route
Axios({
method: "POST",
data: {
username: regUsername,
password: regPassword,
},
withCredentials: true,
url: 'http://localhost:8000/users'
})
.then(res => {
console.log(res)
// window.location = "/"
})
.catch(err => {
console.log(err)
})
}
return (
<div className="Register">
<h1>Register Page</h1><br />
<label>username</label>
<input type="text" id="username" name="username" value={regUsername} onChange={(e) => setRegUsername(e.target.value)} required></input><br></br>
<label>passowrd</label>
<input type="password" id="password" name="password" value={regPassword} onChange={(e) => setRegPassword(e.target.value)} required></input><br />
<button onClick={submitUser}>sign up</button>
<br />
<Link to="/login" type="button"> Login here</Link>
</div>
)
}
export default Register
users.js
const express = require('express')
const router = express.Router()
const userModel = require('../models/userModel')
const bcrypt = require('bcrypt')
// GET users
router.get('/', async (req, res, next) => {
try {
const users = await userModel.find()
res.json(users)
} catch (err) {
res.status(500).json({message: err})
}
})
// POST users to db
router.post('/', (req,res) =>{
// checking if user already exists
userModel.findOne({username: req.body.username}, async (err, user) =>{
// handle any errors
if (err) {
console.log(err);
}
// if username already exists in db
if(user){
res.redirect('http://localhost:3000/')
}
if(!user){
// hashing password before submitted to db
const hashedPass = await bcrypt.hash(req.body.password, 10)
// new instance of user model to be mutated
const User = new userModel({
// grabbing user register data and pushing to mongo
username: req.body.username,
password: hashedPass
})
const newUser = await User.save()
res.json(newUser)
}
})
})
module.exports = router;
By default, a fetch or AJAX call from JavaScript will not follow the redirect header sent from the server. You would have to read the header manually, but from the documentation, it seems like Axios does this for you. Have you tried making the call yet?
If you don't want a full-page, you may need to do this another way with React Router, but the idea is the same, getting the URL from the response headers and supplying that to the React Router history.replace().
When I test sending a request containing both image and text grabbbed from user, it comes through to the backend with proper data when I use Postman. Not from React front-end, though. Request does come through but req.body seems to be empty when I console.log it from backend. What am I doing wrong? I am using Multer.
//FRONT-END
import React, { useState } from 'react';
import axios from 'axios';
const ListProperty = (props) => {
const [address, setAddress] = useState('');
const [file, setFile] = useState(null);
const [filename, setFilename] = useState('Choose File');
const handleAddressChange = (evt) => {
setAddress(evt.target.value);
};
const handlePhotoSelect = (evt) => {
setFile(evt.target.files[0]);
setFilename(evt.target.files[0].name);
};
const handleSubmit = async (evt) => {
evt.preventDefault();
const formData = new FormData();
formData.append('address', address);
formData.append('upload', file);
console.log(formData);
try {
axios.post('http://localhost:3000/listproperty', {
headers: { 'Content-Type': 'multipart/form-data' },
body: formData,
});
} catch (err) {
console.log(err);
}
};
return (
<div>
<h2>Property Listing Form</h2>
<span>Provide property address and Photo</span>
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<button>Click to list</button>
</form>
</div>
);
};
export default ListProperty;
//BACK-END
const express = require('express');
const PropertyModel = require('../models/propertyModel');
const router = new express.Router();
const UserModel = require('../models/userModel');
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/images');
},
filename: function (req, file, cb) {
const uniqueName = `${Math.random().toString(32).slice(2)}.jpg`;
req.image = uniqueName;
cb(null, uniqueName);
},
});
const upload = multer({ storage });
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
console.log('hitting Backend router');
const property = new PropertyModel({
...req.body,
owner: req.user._id,
photo: req.image,
});
await UserModel.findByIdAndUpdate(req.user._id, {
$push: { properties: property._id },
});
try {
await property.save();
res.status(200).send(property);
} catch (err) {
console.log(err);
res.status(400).send(err);
}
}
);
module.exports = router;
If you are sending form data in the body you need to use the formidable npm module
you can install it using npm i formidable
then require formidable at top of the file
var formidable = require("formidable");
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
var form = new formidable.IncomingForm();
form.multiples = false;
form.parse(req, async function (err, fields, files) {
/**now here you can get all files in files and fields with fields
in your case you have sent
formData.append('address', address);
formData.append('upload', file);
above two data in form
so you can get your image from files.upload
and address fields.address **/
})
})
In addition, I would suggest you use Axios for api calls
your axios request is not right. axios post request accepts data as a second argument and third argument is for options ( headers etc ),
axios.post('http://localhost:3000/listproperty', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
another thing is your request is not being triggered at all. try setting input type to submit instead of using the button to trigger onSubmit handler of the form.
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<input type="submit" value="Submit" />
</form>
im building an app with express backend and react frontend but stack on hot to post data to my backnd.
this is my react frontend;
import React from 'react';
import axios from 'axios';
export default class PersonList extends React.Component {
state = {
name: '',
}
handleChange = event => {
this.setState({ name: event.target.value });
}
handleSubmit = event => {
event.preventDefault();
const user = {
name: this.state.name
};
axios.post(`http://localhost:9000/testAPI`, { user })
.then(res => {
console.log(res);
console.log(res.data);
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Person Name:
<input type="text" name="name" onChange={this.handleChange} />
</label>
<button type="submit">Add</button>
</form>
</div>
)
}
}
and this is my backend script testAPI.JS;
const express = require('express');
const axios = require('axios');
const app = express.Router();
app.get('/', function(req, res) {
axios.get('http://localhost:3000/')
.then(function (response) {
console.log(response);
});
});
module.exports=app;
this is the error message im getting;
createError.js:16 Uncaught (in promise) Error: Request failed with status code 404
at createError (createError.js:16)
at settle (settle.js:17)
at XMLHttpRequest.handleLoad (xhr.js:61)
any kind assistance will be apreciated
You need to actually make your express server listen
const express = require('express')
const app = express()
const port = 3000
app.post('/testAPI', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
And I am unsure what to do with the axios call in the backend
Trying to set up a register form using react and then sending the form to my express backed to create the user. The request is getting to the back-end but none of the form data is there.
When i console.log the request body and params, only an empty object gets returned
Here is my react register code
import React from 'react'
class Register extends React.Component {
state = {
name: "",
username: "",
password: ""
}
handleChange = event => {
const { name, value } = event.target
this.setState({[name]:value})
}
handleSubmit = event => {
event.preventDefault();
fetch("/api/register", {
method: "POST",
body: JSON.stringify(this.state)
})
.then((result)=> result.json())
.then((info) => { console.log(info); })
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type="text" name="name" value={this.state.name} placeholder="name" onChange={this.handleChange} />
<input type="text" name="username" value={this.state.username} placeholder="username" onChange={this.handleChange} />
<input type="password" name="password" value={this.state.password} placeholder="password" onChange={this.handleChange} />
<button>Register</button>
</form>
</div>
)
}
}
export default Register
Express code
const express = require("express"),
passport = require("passport"),
User = require("../models/user");
const router = express.Router({mergeParams: true});
// /api before this
router.post("/register", (req, res)=>{
const newUser = new User({
username: req.body.username,
name: req.body.name
});
User.register(newUser, req.body.password, (err, user)=>{
if(err) {
console.log(err);
}
passport.authenticate("local")(req, res, ()=>{
res.redirect("/user/" + user._id);
});
});
});
module.exports = router;
Now im getting a 400 bad request error and another error that says: register:1 Uncaught (in promise) SyntaxError: Unexpected token B in JSON at position 0
I would say that the url from the fetch action is not matching your express route
/api/register > /register
Try adding /api to the express route or remove from the fetch action
I also noticed your route is not returning anything.
You have to return something in the response object.
res.json(newUser);
router.post("/register", (req, res)=>{
const newUser = new User({
username: req.body.username,
name: req.body.name
});
// some code
return res.json(newUser);
});