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

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;

Related

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,
})
);
};

Can't redirect after Mongoose query

I am trying to create a website with user accounts.
But can't redirect after Mongoose save query in register page.
Here is NodeJS index.js file
const express = require('express')
const app = express();
const cors = require('cors');
const mongoose = require('mongoose');
const CryptoJS = require('crypto-js');
const jwt = require('jsonwebtoken');
const dotenv = require('dotenv');
const User = require('./models/User');
dotenv.config();
app.set('view engine', 'ejs');
app.use(cors());
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(express.static('public'));
app.use(express.static('partials'));
app.use(express.static('images'));
app.use(express.static('assets'));
mongoose.connect(process.env.MONGO_URL)
.then(() => {
console.log('DB is connected');
})
.catch((err) => {
console.log(err);
});
app.post('/register', async (req, res) => {
const newUser = new User({
email: req.body.email,
password: CryptoJS.AES.encrypt(req.body.password, process.env.PASS_SEC).toString()
})
try {
const savedUser = await newUser.save();
res.redirect('/')
} catch (err) {
console.log(err)
}
})
(of course app.get('/') and app.get('/login') exists in my index.js file)
My register page (register.ejs)
<%- include('./partials/header') %>
<div class='container'>
<div class="login">
<h1 class='heading'>Register</h1>
<div class='form-block'>
<form action="/register" method="POST">
<label class='label'>Email</label>
<input class='input' type='email' name='email' placeholder='example#mail.com' required/>
<label class='label'>Password</label>
<input class='input' type='password' name='password' placeholder='********' required/>
<input class='button' type='submit' value='Sign Up' />
</form>
</div>
</div>
</div>
<%- include('./partials/footer') %>
Instead of redirect to main page I get json data of saved User.
How to fix that?

How to find whether user has signed up before login, in react, node and mariadb

I am new to react and mariaDB and this is my first attempt. I have created a sign up and sign in form in a single page. I used reactjs for frontend and node for backend and used mariaDB as database. I was able to insert username and password to databse when sign up. Now I want to check whether user has signed up when user try to login. I want to display an error as Wrong combination when username and password is not matched with registered users. I tried to display it, but it doesn't work.
here is my app.js in client side ,
import React, { useState } from 'react';
import './App.css';
import Axios from "axios";
function App() {
const [usernameReg,setUsernameReg]= useState("");
const [passwordReg,setPasswordReg]= useState("");
const [username,setUsername]= useState("");
const [password,setPassword]= useState("");
const [loginStatus,setLoginStatus]= useState("");
const register = () => {
Axios.post("http://localhost:3001/register", {
username:usernameReg,
password:passwordReg,
}).then((response) => {
console.log(response);
});
};
const login = () => {
Axios.post("http://localhost:3001/login", {
username:username,
password:password,
}).then((response) => {
if(response.data.message){
setLoginStatus(response.data.message);
}
else{
setLoginStatus(response.data[0].username);
}
});
};
return (
<div className="App">
<div className='registration'>
<label>Username</label>
<br/>
<input type="text" onChange={(e)=>{
setUsernameReg(e.target.value)
}}>
</input>
<br/>
<label>Password</label>
<br/>
<input type="text" onChange={(e)=>{
setPasswordReg(e.target.value)
}}>
</input>
<br/>
<button onClick={register}>Register</button>
<br/>
</div>
<div className='login'>
<br/>
<label>Username</label>
<br/>
<input type="text"
onChange={(e)=>{
setUsername(e.target.value)
}}
></input>
<br/>
<label>Password</label>
<br/>
<input type="text"
onChange={(e)=>{
setPassword(e.target.value)
}}
></input>
<br/>
<button onClick={login}>Login</button>
</div>
<h1>{loginStatus}</h1>
</div>
);
}
export default App;
here is my index.js in server side
const express = require("express");
const mariadb = require("mariadb");
const cors = require("cors");
const bodyParser = require("body-parser");
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.json());
const db = mariadb.createPool({
user: "root",
host: "localhost",
password: "NishR",
database: "pedro",
});
app.post("/register", async (req, res) => {
const username= req.body.username;
const password = req.body.password;
db.query(
"INSERT INTO user (username,PASSWORD) VALUES(?,?)",
[username,password],
(err,result)=>{
console.log(err);
}
);
}
);
app.post("/login", async (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.length>0){
res.send(result);
}else{
res.send({message:"wrong combination"});
}
}
);
}
);
app.listen(3001, () => {
console.log("Server is running");
});
please help me to find the error.

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

Resources