CORS issue with nodejs and react - node.js

Good day to all of you. I'm trying to build a log in and right now im struggling with the register form, I'm using react js for FrontEnd and nodejs/express for BackEnd.
I created the register form, I'm putting data from this register form inside an object, parsing it to a JSON obj and sending it to the node server through fetch api, however the cors thing is not letting this happen, i'm not sure why. The next is the code from the register(front side)
import React, { useState } from 'react';
import './Register.css';
import Title from './Title/Title';
import Label from './Label/Label';
import Input from './Input/Input';
function Register(){
const [ name, setName ] = useState('');
const [ lastname, setLastname ] = useState('');
const [ email, setEmail ] = useState('');
const [ username, setUsername ] = useState('');
const [ password, setPassword ] = useState('');
function handleChange(name, value){
if(name==='name')setName(value);
else if (name==='lastname')setLastname(value);
else if (name==='email')setEmail(value);
else if (name==='username')setUsername(value);
else if (name==='password')setPassword(value);
}
function handleSubmit(e){
e.preventDefault();
let account = { name, lastname, email, username, password };
if(account)console.log(account);
var url = 'http://localhost:3030/';
fetch(url, {
method: 'OPTION',
body: JSON.stringify(account),
headers:{
'Content-Type': 'application/json'
}
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
///////////////////////////////////////////////////////////////////////////////////
}
return(
<div className='register-div'>
<Title text='Register Form'/>
<div className='squareout'>
<div className='square'>
<Label text='Name'/>
<Input attribute={{type:'text', name:'name'}} handleChange={handleChange}/>
</div>
<div className='square'>
<Label text='Last Name'/>
<Input attribute={{type:'text', name:'lastname'}} handleChange={handleChange}/>
</div>
<div className='square'>
<Label text='E-Mail'/>
<Input attribute={{type:'text', name:'email'}} handleChange={handleChange}/>
</div>
<div className='square'>
<Label text='Username'/>
<Input attribute={{type:'text', name:'username'}} handleChange={handleChange}/>
</div>
<div className='square'>
<Label text='Password'/>
<Input attribute={{type:'password', name:'password'}} handleChange={handleChange}/>
</div>
</div>
<button className='boton' onClick = {handleSubmit}>Register</button>
</div>
)
};
export default Register;
and this is from the server side:
const express = require('express');
const app = express();
const port = 3030;
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
app.use(express.static('public'));
app.use(function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,DELETE");
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Access-Control-Allow-Headers, Content-Type, Authorization, Origin, Accept");
res.setHeader('Access-Control-Allow-Credentials', true)
next();
});
app.use(express.json());
app.post('http://localhost:3030/', (request, response) => {
console.log(request.body);
const data = request.body;
response.json({
name: data.name,
lastname: data.lastname,
email: data.email,
username: data.username,
password: data.password
})
});
and yet I'm getting this error:
Line 33: fetch(url, {
this is from the first code, the front side

Instead of reinventing the wheel, you can use CORS
And simply do:
const cors = require('cors')
const app = express()
app.use(cors())

Simply when facing an CORS issue remember these concepts,
It is not front-end apps fault (mostly)
you are trying to cross domain requests
those are blocked by default
You can allow this by allowOrigins with backend (syntax varies on language)
problem here is accessing localhost:3030 from localhost:3000
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors())
//for single origin
var corsOptions = {
origin: 'http://localhost:8080',
optionsSuccessStatus: 200 // For legacy browser support
}
app.use(cors(corsOptions));
source

If you set Access-Control-Allow-Credentials to true means you are allowing credentialed request
res.setHeader('Access-Control-Allow-Credentials', true)
Then you can't set Access-Control-Allow-Origin to *(wildcard)
res.setHeader("Access-Control-Allow-Origin", "*");
so solution here is that,
you have to set specific origin here,(frontend app origin for reactjs by default its http://localhost:3000/)
res.setHeader("Access-Control-Allow-Origin", "http://localhost:3000/");

In the end I found the solution, first instead of the method 'OPTION' I used 'POST' and the on the server side, i changed 'http://localhost:3030/' to just '/' and worked.

Related

How to send state data to a node file

I've created a little React/Node app and I want to send a certain piece of data from the React frontend to the Node backend. The data is based on what you type in an input and saved as a state inputCardHolder, I want to now send that data to the other file, which is non-React, to be used there.
How would I do this?
App.js(React)
import React, { useState } from 'react';
import axios from 'axios'
import './App.css';
function App() {
const [cardHolder, setCardHolder] = useState("");
const [inputCardHolder, setinputCardHolder] = useState("");// This is the state I wanna send
const [cardNumber, setCardNumber] = useState("");
const [balance, setBalance] = useState("");
const [expDate, setExpDate] = useState("");
const [cvc, setCvc] = useState("");
function getCardData() {
axios.get("http://localhost:5000/", { crossdomain: true })
.then(response => {
setCardHolder(response.data.data.name_on_card);
setCardNumber(response.data.data.card_pan);
setBalance(response.data.data.amount + " " + response.data.data.currency);
setExpDate(response.data.data.expiration);
setCvc(response.data.data.cvv);
})
};
const handleInputChange = (event) => {
setinputCardHolder(event.target.value);
};
return (
<div>
<div className='vcard'>
<div className='theBalance'>
<h2>{balance}</h2>
</div>
<div className='numNcvc'>
<h2 className='theNum'>{cardNumber}</h2>
<h2 className='theCvc'>{cvc}</h2>
</div>
<div className='expNholder'>
<h2>Expiry Date<br/> {expDate}</h2>
<h2>Card Holder<br/> {inputCardHolder}</h2>
</div>
</div>
<div className='details-div'>
<form className='details'>
<input
placeholder='Name on Card'
type="text"
id='cardholder'
name='cardholder'
onChange={handleInputChange}
value={inputCardHolder}></input>
<input placeholder='Amount (in USD)' type="text"></input>
<input placeholder='MTN MoMo Number' type="text"></input>
</form>
<button className='createCardBtn' onClick={getCardData}>
Create Card
</button>
</div>
</div>
);
}
export default App;
nodecard.js(Node Backend)
const { response } = require('express');
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const Flutterwave = require('flutterwave-node-v3');
const flw = new Flutterwave("FLWPUBK_TEST-63a79c5a6fe457d75a611b0f376e3e53-X", "FLWSECK_TEST-a6281194ef4ca095e794a1681fe32d69-X");
// Payload: Flutterwave Card Details
const payload = {
"currency": "USD",
"amount": 50,
"billing_name": "Daniel Odd",// I need the 'inputCardHolder' data to be here
"billing_address": "2014 Forest Hills Drive",
"billing_city": "React",
"billing_state": "NY",
"billing_postal_code": "000009",
"billing_country": "US",
}
flw.VirtualCard.create(payload)
.then(response => {
console.log(response);
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();
});
app.get("/", (req, res) => {
res.send(response)
});
});
/*const wordy = "Another word"
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();
});
app.get("/", (req, res) => {
res.send(wordy)
});*/
app.listen(5000, () => {console.log("Server started on port 5000")})
//createVcard();

Getting 404 onSubmit using React, Express

I am developing a simple MERN app. I am trying to connect the both ends and post data on server onSubmit(). I am able to access the server URL directly.However, I am getting POST http://localhost:1337/api/register 404 (Not Found) when requesting it from React page.
Any kind of help will be appreciated
Thanks
server/index.js
const express = require('express')
const app = express()
const cors = require('cors')
app.use(cors())
app.use(express.json())
app.get('/api/register', (req, res) =>{
console.log(req.body)
res.json({status:'ok'})
})
app.listen(1337,() =>{
console.log('Server started on 1337')
})
client/App.js
import './App.css';
import {useState} from 'react';
function App() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
async function registerUser(event){
event.preventDefault()
const response = await fetch('http://localhost:1337/api/register',{
method:'POST',
headers:{
'Content-Type': 'application/json',
},
body:JSON.stringify({
name,
email,
password
}),
})
const data = await response.json()
console.log(data)
}
return (
<div>
<h1>Register</h1>
<form onSubmit = {registerUser}>
<input
value = {name}
onChange = {(e)=> setName(e.target.value)}
type="text"
placeholder="Name"/>
<br/>
<input
value = {email}
onChange = {(e)=> setEmail(e.target.value)}
type="email"
placeholder="Email"/>
<br/>
<input
value = {password}
onChange = {(e)=> setPassword(e.target.value)}
type="password"
placeholder="Password"/>
<br/>
<input type = "submit" value = "Register"/>
</form>
</div>
);
}
export default App;
Thanks
const express = require('express')
const app = express()
const cors = require('cors')
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: false }));
app.post('/api/register', (req, res) =>{
console.log(req.body)
res.json({status:'ok'})
})
app.listen(1337,() =>{
console.log('Server started on 1337')
})
For the post request, we must tell our API requests that it is POST so we must initialize our request with the POST method.

Unable to use the data sent to the server

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("/");
});

How to send data from React to Express

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 πŸ™.

Express new session on a request from frontend app

I am creating a login page, and the problem is that express creating a new session each time a request comes from the frontend app. I have checked and tried all the other answers on the net and here at SO. The part that bugs me most is that it works with curl, but not via the frontend app.
Here is the code:
server.js
const bodyParser = require('body-parser');
const cors = require('cors');
const session = require('express-session')
const cookieParser = require('cookie-parser');
const express = require('express');
const app = express();
app.use(cors());
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({extended: true})); // for parsing application/x-www-form-urlencoded
app.use(cookieParser('keyboard cat'));
app.set('trust proxy', true);
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
httpOnly: true,
cookie: {secure: false}
}));
app.get('/secured', function (req, res) {
res.header("Access-Control-Allow-Origin", "http://localhost:3006");
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Cookie");
if (req.session) {
console.log("has session", req.session);
if (req.session.loggedInUser) {
res.status(200).end("OK");
} else {
res.status(401).end("NOTOK1");
}
} else {
res.status(401).end("NOTOK2");
}
});
const users = [
{email: 'foo#bar.com', pass: 'foo'}
];
app.post('/login', function (req, res) {
const matched = users.filter(e => e.email === req.body.loginEmail && e.pass === req.body.loginPassword);
if (matched === undefined || matched.length === 0) {
res.status(401).end('NOTOK');
} else {
req.session.loggedInUser = matched[0];
req.session.save();
res.status(200).end('OK');
}
});
app.listen(8000, () => {
console.log('Started, listening');
});
And the login component from the frontend (React).
import React from "react";
import {MDBBtn, MDBCol, MDBContainer, MDBRow} from 'mdbreact';
import axios from 'axios';
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {error:{}};
this.handleLogin = this.handleLogin.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleLogin(event) {
event.preventDefault();
let state = this.state;
let registerFormInputs = Object.keys(this.state).filter(v => v.startsWith("login"));
const data = {};
registerFormInputs.forEach(function (input) {
data[input] = state[input];
});
axios.post('http://localhost:8000/login', data)
.then(function (response) {
axios.get('http://localhost:8000/secured', {withCredentials: true})
.then(resp => {
console.log(resp);
});
})
.catch(err => {
this.setState({error: err.response})
})
}
handleChange(event) {
this.setState({[event.target.id]: event.target.value});
}
render() {
return (
<MDBContainer>
<MDBRow>
<MDBCol md="6">
<form onSubmit={this.handleLogin}>
<p className="h4 text-center mb-4">Log in</p>
{this.state.error.data ? <div>{this.state.error.data}</div> : null}
<label htmlFor="loginEmail" className="grey-text">
Email
</label>
<input
type="email"
id="loginEmail"
className="form-control"
onChange={this.handleChange}
/>
<br/>
<label htmlFor="loginPassword" className="grey-text">
Password
</label>
<input
type="password"
id="loginPassword"
className="form-control"
onChange={this.handleChange}
/>
<div className="text-center mt-4">
<MDBBtn color="indigo" type="submit">Login</MDBBtn>
</div>
</form>
</MDBCol>
</MDBRow>
</MDBContainer>
);
}
}
export default Login;
The frontend:
https://codesandbox.io/s/2vor7xproj
The backend:
https://codesandbox.io/s/j755x416j9
I dont generally handle sessions directly. I let passportJS deal with that.
However, in the session constructor saveUninitialized is generally set to false when dealing with login events or trying to deal with race conditions in parallel requests.
From express-session npm
saveUninitialized
Forces a session that is "uninitialized" to be saved to the store. A session is uninitialized when it is new but not modified. Choosing false is useful for implementing login sessions, reducing server storage usage, or complying with laws that require permission before setting a cookie. Choosing false will also help with race conditions where a client makes multiple parallel requests without a session.
The default value is true, but using the default has been deprecated, as the default will change in the future. Please research into this setting and choose what is appropriate to your use-case.

Resources