React + Express sending data? - reactjs

I'm working on a login/register system with React front-end and Node/Express back-end.
When the registration fails, i want to reload the registration page and return the error msgs to my react front end.
Instead of refreshing the page, it renders the JSON data.
How can you go about re-rendering the registration form, while sending the data behind the scenes?
router.post("/register", (req, res) => {
const { name, email, password, password2 } = req.body;
let errors = [];
...
...
...
if (errors.length > 0) {
res.send({
errors,
name,
email,
password,
password2
});

You need to receive the response from express in your react app, and perform your logic inside your submit function in react.

One way you can do this is to create a state variable for the error message. Make the initial value null or ''. When user registration fails catch the error and assign it your error variable in state. Here is an example of user registration system:
//RegistrationForm.js
import React, { useState } from 'react';
import AuthApiService from '../../services/auth-api-service';
import { Input } from '../Utils/Utils';
import { Button } from '#material-ui/core';
import './RegistrationForm.css';
export default function RegistrationForm(props) {
const [error, setError] = useState(null)
const errorDiv = error
? <div className="error">
<i class="material-icons error-icon">error_outline</i>
{error}
</div>
: '';
const handleSubmit = e => {
e.preventDefault();
setError(null);
const { full_name, user_name, password } = e.target;
AuthApiService.postUser({
full_name: full_name.value,
user_name: user_name.value,
password: password.value
})
.then(user => {
full_name.value = '';
user_name.value = '';
password.value = '';
props.onRegistrationSuccess();
})
.catch(res => {
setError(res.error);
})
};
return(
<form className='RegistrationForm'
onSubmit={handleSubmit}
>
<div className='full_name'>
<label htmlFor='RegistrationForm__full_name'>
Full name
</label>
<Input
name='full_name'
type='text'
required
id='RegistrationForm__full_name'>
</Input>
</div>
<div className='user_name'>
<label htmlFor='RegistrationForm__user_name'>
User name
</label>
<Input
name='user_name'
type='text'
required
id='RegistrationForm__user_name'>
</Input>
</div>
<div className='password'>
<label htmlFor='RegistrationForm__password'>
Password
</label>
<Input
name='password'
type='password'
required
id='RegistrationForm__password'
>
</Input>
</div>
<div className='confirm-password'>
<label htmlFor="LoginForm__confirm-password">
Retype Password
</label>
<Input
name='confirm-password'
type="password"
required
id="LoginForm__confirm-password">
</Input>
</div>
{errorDiv}
<Button type='submit' variant='contained' color='default'>
Register
</Button>
</form>
)
}
//AuthApiService.js
import config from '../config';
import TokenService from './token-service';
import IdleService from './idle-service';
const AuthApiService = {
postUser(user) {
return fetch(`${config.API_ENDPOINT}/users`, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(user)
})
.then(res =>
(!res.ok)
? res.json().then(e => Promise.reject(e))
: res.json()
)
.then(res => {
TokenService.saveAuthToken(res.authToken)
IdleService.registerIdleTimerResets()
TokenService.queueCallbackBeforeExpiry(() => {
AuthApiService.postRefreshToken()
})
return res
})
},
postLogin({user_name, password }) {
return fetch(`${config.API_ENDPOINT}/auth/login`, {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({user_name, password}),
})
.then(res =>
(!res.ok)
? res.json().then(e => Promise.reject(e))
: res.json()
)
.then(res => {
TokenService.saveAuthToken(res.authToken)
TokenService.queueCallbackBeforeExpiry(() => {
AuthApiService.postRefreshToken()
})
return res
})
},
postGuestLogin() {
return fetch(`${config.API_ENDPOINT}/auth/guest-login`, {
method: 'POST',
headers: {
'content-type': 'application/json'
},
})
.then(res =>
(!res.ok)
? res.json().then(e => Promise.reject(e))
: res.json()
)
.then(res => {
TokenService.saveAuthToken(res.authToken)
TokenService.queueCallbackBeforeExpiry(() => {
AuthApiService.postRefreshToken()
})
return res
})
},
postRefreshToken() {
return fetch(`${config.API_ENDPOINT}/auth/refresh`, {
method: 'POST',
headers: {
'authorization': `Bearer ${TokenService.getAuthToken()}`,
},
})
.then(res =>
(!res.ok)
? res.json().then(e => Promise.reject(e))
: res.json()
)
.then(res => {
TokenService.saveAuthToken(res.authToken)
TokenService.queueCallbackBeforeExpiry(() => {
AuthApiService.postRefreshToken()
})
return res
})
.catch(err => {
TokenService.clearAuthToken()
console.log('refresh token request error')
console.log(err)
})
}
}
export default AuthApiService;
//TokenService.js
import jwtDecode from 'jwt-decode';
import config from '../config';
let _timeoutId;
const _TEN_SECONDS_IN_MS = 10000;
const TokenService = {
saveAuthToken(token) {
window.localStorage.setItem(config.TOKEN_KEY, token);
},
getAuthToken() {
return window.localStorage.getItem(config.TOKEN_KEY);
},
clearAuthToken() {
window.localStorage.removeItem(config.TOKEN_KEY);
},
hasAuthToken() {
return !!TokenService.getAuthToken()
},
makeBasicAuthToken(userName, password) {
return window.btoa(`${userName}:${password}`);
},
parseJwt(jwt) {
return jwtDecode(jwt);
},
readJwtToken() {
return TokenService.parseJwt(TokenService.getAuthToken());
},
_getMsUntilExpiry(payload) {
return (payload.exp * 1000) - Date.now();
},
queueCallbackBeforeExpiry(callback) {
const msUntilExpiry = TokenService._getMsUntilExpiry(
TokenService.readJwtToken()
);
_timeoutId = setTimeout(callback, msUntilExpiry - _TEN_SECONDS_IN_MS);
},
clearCallbackBeforeExpiry() {
clearTimeout(_timeoutId);
}
};
export default TokenService;
EDIT:
Here is an example of what the router can look like:
//users-router.js
const express = require('express')
const path = require('path')
const UsersService = require('./users-service')
const AuthService = require('../auth/auth-service')
const usersRouter = express.Router()
const jsonBodyParser = express.json()
usersRouter.post('/', jsonBodyParser, (req, res, next) => {
const { password, user_name, full_name } = req.body
for (const field of ['password', 'full_name', 'user_name'])
if (!req.body[field])
return res.status(400).json({
error: `Missing ${field} in request body`
})
const passwordError = UsersService.validatePassword(password)
if (passwordError)
return res.status(400).json({ error: passwordError })
UsersService.hasUserWithUserName(
req.app.get('db'),
user_name
)
.then(hasUserWithUserName => {
if(hasUserWithUserName)
return res.status(400).json({ error: 'Username already taken' })
return UsersService.hashPassword(password)
.then(hashedPassword => {
const newUser = {
user_name,
full_name,
password: hashedPassword,
date_created: 'now()'
}
return UsersService.insertUser(
req.app.get('db'),
newUser
)
.then(user => {
const serializedUser = UsersService.serializeUser(user)
const { user_name, user_id } = serializedUser
const sub = user_name
const payload = {user_id: user_id}
res
.status(201)
.location(path.posix.join(req.originalUrl, `/${user.id}`))
.send({
authToken: AuthService.createJwt(sub, payload),
user: serializedUser
})
})
})
})
.catch(next)
})
module.exports = usersRouter
//users-service.js
const bcrypt = require('bcryptjs')
const xss = require('xss')
const REGEX_UPPER_LOWER_NUMBER_SPECIAL = /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##\$%\^&])[\S]+/
const UsersService = {
hasUserWithUserName(db, user_name) {
return db('askify_users')
.where({ user_name })
.first()
.then(user => !!user)
},
insertUser(db, newUser) {
return db
.insert(newUser)
.into('askify_users')
.returning('*')
.then(([user]) => user)
},
validatePassword(password) {
if (password.length < 8) {
return 'Password must be longer than 8 characters'
}
if (password.length > 72) {
return 'Password must be less than 72 characters'
}
if (password.startsWith(' ') || password.endsWith(' ')) {
return 'Password must not start or end with empty spaces'
}
if (!REGEX_UPPER_LOWER_NUMBER_SPECIAL.test(password)) {
return 'Password must contain one upper case, lower case, number and special character'
}
return null
},
hashPassword(password) {
return bcrypt.hash(password, 12)
},
serializeUser(user) {
return {
id: user.id,
full_name: xss(user.full_name),
user_name: xss(user.user_name),
nickname: xss(user.nick_name),
date_created: new Date(user.date_created),
}
},
}
module.exports = UsersService
// auth-service.js
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
const config = require('../config')
const AuthService = {
getUserWithUserName(db, user_name) {
return db('askify_users')
.where({user_name})
.first()
},
comparePasswords(password, hash) {
return bcrypt.compare(password, hash)
},
createJwt(subject, payload) {
return jwt.sign(payload, config.JWT_SECRET, {
subject,
expiresIn: config.JWT_EXPIRY,
algorithm: 'HS256',
})
},
verifyJwt(token) {
return jwt.verify(token, config.JWT_SECRET, {
algorithms: ['HS256'],
})
},
parseBasicToken(token) {
return Buffer
.from(token, 'base64')
.toString()
.split(':')
},
}
module.exports = AuthService

Related

How do I persist my next-auth user session? so i could use the ID provided to fetch data in other routes

What I want to achieve here is, whenever a user logs in, I want to store the data returned because the data holds an ID that I would use to fetch data in other routes.
When a user successfully logs in, he would be redirected to the /home route and the ID gotten from the session would be used to fetch data. Everything works fine initially, but if I refresh the home page, the user becomes null.
This is what my [...nextauth].js looks like.
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import axios from "axios";
export default NextAuth({
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: { label: "Username", type: "text", placeholder: "justin" },
password: {label: "Password",type: "password",placeholder: "******"},
},
async authorize(credentials, req) {
const url = req.body.callbackUrl.split("/auth")[0];
const { username, password } = credentials;
const user = await axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
})
.then((res) => {
return res.data;
})
.catch((err) => {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
return null;
});
return user;
},
}),
],
callbacks: {
jwt: ({ token, user }) => {
if (user) {
token.user = user;
}
return token;
},
session: ({ session, token }) => {
if (token) {
session.user = token.user;
}
return session;
},
},
pages: {
signIn: "/auth/login",
newUser: "/auth/register",
},
});
and this is what my /home route looks like
import Card from "#/components/card/Card";
import React, { useEffect, useState } from "react";
import styles from "./home.module.css";
import { Ubuntu } from "#next/font/google";
import { useSession } from "next-auth/react";
import { useDispatch, useSelector } from "react-redux";
const ubuntu = Ubuntu({ weight: "500", subsets: ["cyrillic"] });
const getData = async (id) => {
const res = await fetch({
url: "http://localhost:3000/api/note/getall",
method: "POST",
"content-type": "application/json",
data: {
id: id,
},
});
if (!res.ok) {
console.log(id);
throw new Error("Unable to fetch");
} else {
return res.json();
console.log(res);
}
};
function home() {
const colors = ["#E9F5FC", "#FFF5E1", "#FFE9F3", "#F3F5F7"];
const random = Math.floor(Math.random() * 5);
const rc = colors[random];
const [pop, setPop] = useState("none");
const { user } = useSelector((state) => state.user);
const getDataa = async () => {
console.log(user)
const data = await getData(user._id);
console.log(data);
};
useEffect(() => {
if (user) {
alert(user)
}
}, []);
return (
<div className={styles.home}>
<header>
<h3 className={ubuntu.className}>
Hello, <br /> {user?.username}!
</h3>
<input type="text" placeholder="search" />
</header>
<div className={styles.nav}>
<h1 className={ubuntu.className}>Notes</h1>
</div>
<div className={styles.section}>
<div className={styles.inner}>
{/* {data &&
data.map((e) => (
<Card
rawData={e}
color={colors[Math.floor(Math.random() * colors.length)]}
/>
))} */}
</div>
</div>
<div className="new"></div>
</div>
);
}
export default home;
Add this component to your App.js file :
function Auth({ children }) {
const router = useRouter();
const { status } = useSession({
required: true,
onUnauthenticated() {
router.push("/sign-in");
},
});
if (status === "loading") {
return <div>Loading ...</div>;
}
return children;
}
Now in your App function instead of returning <Component {...pageProps} /> you check first if the component has auth property, so you wrapp it with <Auth> to ensure that every component that requires session will only mount when the session finishes loading (that's why the user is null because the session is still loading)
{
Component.auth ? (
<Auth>
<Component {...pageProps} />
</Auth>
) : (
<Component {...pageProps} />
);
}
finally you add .auth = {} to every page in whitch you want the session to be defined (Home in your case)
const Home = () => {
//....
}
Home.auth = {};
This also helps to redirect user to /sign-in page if the session is expired
This code seems like it would create a problem / race-condition since you're mixing two different async promise handling styles:
const user = await axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
})
.then((res) => {
return res.data;
})
.catch((err) => {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
return null;
});
return user;
It should either be this:
try {
const user = await axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
});
return user.data;
} catch (err) {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
}
Or this:
axios({
url: `${url}/api/user/login`,
method: "POST",
data: {
username: username,
password: password,
},
"content-type": "application/json",
}).then((res) => {
return res.data;
}).catch((err) => {
if (err.response.data) {
throw new Error(err.response.data);
} else {
return null;
}
return null;
});

function is returning undefined while I am rexpecting some data

App.js
import { Component } from "react";
import "./App.css";
import checkUser from "./functions";
class App extends Component {
constructor() {
super();
console.log(checkUser('',''));
}
render() {
return (
<>
<h1 id="name">{this.user}</h1>
</>
);
}
}
export default App;
functions.js
let checkUser = (username, password) => {
let local_username = localStorage.getItem("username");
let local_token = localStorage.getItem("token");
let local_client_id = localStorage.getItem("client_id");
if (username === "" && password === "") {
fetch("/login?user=auth", {
method: "POST",
headers: {
username: local_username,
token: local_token,
client_id: local_client_id,
},
})
.then((data) => {
data.json();
})
.then((data) => {
return data
});
} else if (
username !== null &&
password !== null &&
local_client_id === null
) {
let res = fetch("/login?user=notloggedin", {
method: "POST",
headers: { username: username, password: password },
})
.then((res) => res.json())
.then((data) => {
if (data.status === true) {
localStorage.setItem("username", username);
localStorage.setItem("token", data.token);
localStorage.setItem("client_id", data.client_id);
return true;
} else {
return false;
}
});
}
};
export default checkUser;
I am expecting checkUser function to log user name but it is logging undefined.
It shows to request to server but not returning, I think that the problem is with the code in functions.js where I am returning something in a .then funtion.
In the first .then() you forget to return data.json(). you can either return:
.then((data) => {
return data.json();
})
//or wrap it in brackets so it automatically returns:
.then((data) => (
data.json();
))
//or make it one line:
.then((data) => data.json())

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

MERN Stack/REDUX -- Attempting to send user data for signup/registration from my React app to express and into my mongoDB but keep receiving a 404 error in letting me know that the page doesnt exist. I've been able to connect it before on a previous application but having trouble now.
Server.js file:
const express = require("express");
const connectDB = require("./config/db");
const path = require("path");
const bodyParser = require("body-parser");
const app = express();
//Connecting to the DB \\
connectDB();
// Middleware Init\\
app.use(express.json({ extended: false }));
app.use(bodyParser.json());
// Routes \\
app.use("/api/users", require("./routes/users"));
app.use("/api/auth", require("./routes/auth"));
// Static assets if in production (deployment purposes) \\
if (process.env.NODE_ENV === "production") {
// Set static folder \\
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
}
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Users Route:
const express = require("express");
const router = express.Router();
const { check, validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const config = require("config");
const auth = require("../middleware/auth");
//importing User Schema \\
const User = require("../models/User");
// #route POST api/users
// #desc Register user
// #access Public
router.post(
"/", [
check("firstname", "First Name is Required").not().isEmpty(),
check("lastname", "Last Name is Required").not().isEmpty(),
check("email", "Email is Required").isEmail(),
check(
"password",
"Please enter a password with 5 or more character"
).isLength({ min: 5 }),
],
async(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// just deconstruct firstname, lastname, email, and password, isAdmin from req.body \\
const { firstname, lastname, email, password, isAdmin } = req.body;
try {
// see if user exists \\
let user = await User.findOne({ email });
if (user) {
return res
.status(400)
.json({ errors: [{ msg: "User already exists" }] });
}
user = new User({
firstname,
lastname,
email,
password,
isAdmin,
});
// Encrypt password \\
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
// Return jsonwebtoken
const payload = {
user: {
id: user.id,
},
};
// Login good for 1 hour \\
jwt.sign(
payload,
config.get("jwtSecret"), { expiresIn: 36000 },
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
// #route PUT api/users/address
// #desc Add/edit address
// #access Private
router.put(
"/address", [
auth, [
check("street", "Street is required").not().isEmpty(),
check("country", "Country is required").not().isEmpty(),
check("city", "City is required").not().isEmpty(),
check("state", "State/Province is required").not().isEmpty(),
check("zipcode", "Zip/Postal Code is required").not().isEmpty(),
],
],
async(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { street, country, city, state, zipcode } = req.body;
const newAddress = {
street,
country,
city,
state,
zipcode,
};
try {
// look for the user with the id and then update the address
await User.updateOne({ _id: req.user.id }, {
$set: {
address: newAddress,
},
});
res.json({ msg: "Successfully saved your address." });
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
module.exports = router;
DB Configuration(db.js):
const mongoose = require("mongoose");
const config = require("config");
const db = config.get("mongoURI");
const connectDB = async() => {
try {
await mongoose.connect(db, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
});
console.log("MongoDB Connected!..");
} catch (err) {
console.error(err.message);
//exit process with failure\\
process.exit(1);
}
};
module.exports = connectDB;
DB configuration(default.json)
{
"mongoURI": "mongodb+srv://AJ****:*********#sustdropclust.zcc0v.mongodb.net/*******?
retryWrites=true&w=majority",
"jwtSecret": "**********"
}
Middleware:
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = function(req, res, next) {
// Get token from header \\
const token = req.header("x-auth-token");
// check if no token \\
if (!token) {
return res.status(401).json({ msg: "No token, auth denied" });
}
// Verify token \\
try {
const decoded = jwt.verify(token, config.get("jwtSecret"));
req.user = decoded.user;
next();
} catch (err) {
res.status(401).json({ msg: "Token not valid" });
}
};
Redux Actions(auth.js):
import axios from "axios";
import { setAlert } from "./alert";
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
MAKE_ADMIN,
RESET_RECENT_ORDERS_LOADING,
} from "./types";
import setAuthToken from "../utilities/setAuthToken";
// Load User
export const loadUser = () => async(dispatch) => {
if (localStorage.token) {
setAuthToken(localStorage.token);
}
try {
const res = await axios.get("/api/auth");
if (res.data.isAdmin) {
dispatch({ type: MAKE_ADMIN });
}
dispatch({
type: USER_LOADED,
payload: res.data,
});
} catch (err) {
dispatch({
type: AUTH_ERROR,
});
}
};
// Register User
export const register = ({ firstname, lastname, email, password }) => async(
dispatch
) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify({ firstname, lastname, email, password });
try {
const res = await axios.post("/api/users", body, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
dispatch(setAlert("Registration is successful", "success"));
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: REGISTER_FAIL,
});
}
};
// Login User
export const login = (email, password) => async(dispatch) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify({ email, password });
try {
const res = await axios.post("/api/auth", body, config);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
dispatch(setAlert("Welcome! Login Successful!", "success"));
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: LOGIN_FAIL,
});
}
};
// Logout / Clear Profile
export const logout = () => (dispatch) => {
dispatch({ type: LOGOUT });
// Resets the loading for recent orders incase another user logs in without refreshing the page
dispatch({ type: RESET_RECENT_ORDERS_LOADING });
};
// Add or Edit Address - Add address if it is null, edit it otherwise.
export const addAddress = ({ formData }) => async(dispatch) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
const res = await axios.put("/api/users/address", formData, config);
dispatch(loadUser());
dispatch(setAlert(res.data.msg, "success"));
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
}
};
Redux Reducers(auth.js):
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
MAKE_ADMIN,
} from "../actions/types";
//add isAdmin later to authenticate if the current user is an admin (maybe just check user.isAdmin
once we get the user and put it in the state)
const initialState = {
token: localStorage.getItem("token"),
isAuthenticated: false,
loading: true,
user: null,
isAdmin: false,
};
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case USER_LOADED:
return {
...state,
isAuthenticated: true,
loading: false,
user: payload,
};
case REGISTER_SUCCESS:
case LOGIN_SUCCESS:
localStorage.setItem("token", payload.token);
return {
...state,
...payload,
isAuthenticated: true,
loading: false,
};
case REGISTER_FAIL:
case LOGIN_FAIL:
case AUTH_ERROR:
case LOGOUT:
localStorage.removeItem("token");
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
isAdmin: false,
user: null,
};
case MAKE_ADMIN:
return {
...state,
isAdmin: true,
};
default:
return state;
}
}
Registration page(register.js):
import React, { useState } from "react";
import { Link, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import { setAlert } from "../../actions/alert";
import { register } from "../../actions/auth";
import PropTypes from "prop-types";
import "./Login.css";
const Register = ({ setAlert, register, isAuthenticated }) => {
const [formData, setFormData] = useState({
firstname: "",
lastname: "",
email: "",
password: "",
password2: "",
});
const { firstname, lastname, email, password, password2 } = formData;
// it changes the value of the target every keystroke
const onChange = (e) =>
setFormData({...formData, [e.target.name]: e.target.value });
const onSubmit = (e) => {
e.preventDefault();
if (password !== password2) {
setAlert("Passwords do not match", "danger");
} else {
register({ firstname, lastname, email, password });
}
};
// Redirects to /account after a successful registration
if (isAuthenticated) {
return <Redirect to = "/account" / > ;
}
return (
<div className = "wrapper-register" >
<h1 className = "large text-dark" > create an account < /h1>
<form onSubmit = {(e) => onSubmit(e)}>
<div className = "form-group" >
<label htmlFor = "firstname" > first name < /label>
<input className = "form-control" type = "text" name = "firstname" value = { firstname } onChange = {(e) => onChange(e)}/>
</div>
<div className = "form-group" >
<label htmlFor = "lastname" > last name </label>
<input className = "form-control" type = "text" name = "lastname" value = { lastname } onChange = {(e) => onChange(e)}/>
</div>
<div className = "form-group" >
<label htmlFor = "email" > email </label>
<input className = "form-control" type = "email" name = "email" value = { email } onChange = {(e) => onChange(e)}/>
</div>
<div className = "form-group" >
<label htmlFor = "password" > password </label>
<input className = "form-control" type = "password" name = "password" value = { password } onChange = {(e) => onChange(e)} minLength = "6" />
</div>
<div className = "form-group" >
<label htmlFor = "password" > confirm password < /label> < input className = "form-control" type = "password" name = "password2" value = { password2 } onChange = {(e) => onChange(e)}minLength = "6" />
</div>
<div className = "register-button" >
<input type = "submit" className = "btn btn-dark btn-block" value = "Register" />
</div>
</form >
<p>Already have an account ? < Link to = "/account/login" > Sign In </Link> </p>
</div>
);
};
Register.propTypes = {
setAlert: PropTypes.func.isRequired,
register: PropTypes.func.isRequired,
};
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated,
});
export default connect(mapStateToProps, { setAlert, register })(Register);
I noticed that you are running the server on port 5000, i guess that might have caused the error since you are sending a POST request to port 3000 instead. Unless you specified a port in process.env.PORT of 3000.
Figured it out. When you create a react app through the create-react-app cli you have to specify and set your proxy in package.json like so "proxy": "http://localhost:4000". This way, when you fetch('/api/signup') in development, the development server will recognize that it’s not a static asset, and will proxy your request to http://localhost:4000/api/signup
in Redux Actions(auth.js) you can set base URL path in axios defaults (just after imports).
window.axios.defaults.baseURL = (process.env.NODE_ENV !== 'production') ? 'your production server url' : 'http://localhost:5000/';
Restart your frontend. If incase you are using react, just stop it from running and re run again! Problem solved!!!

Express server "failed to load resource: the server responded with a status of 500" after fetch request to server. How do I fix this error?

When a user enters their email and password then click login a fetch request is made to my server:
const RegistrationForm = (props) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleLoginSuccess = (props) => {
return props.history.push("./homepage");
};
const handleSubmit = (e) => {
e.preventDefault();
authApiService
.userLogin({ email, password })
.then((res) => {
password.value = "";
TokenService.saveAuthToken(res.authToken);
handleLoginSuccess();
})
.catch((res) => {
console.log(res.error);
});
};
return (
<form onSubmit={handleSubmit}>
<fieldset>
<div className="form-group">
<div>
<label htmlFor="registration-email">Email</label>
</div>
<EmailInput
value={email}
handleChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="form-group">
<div>
<label htmlFor="registration-password">Password</label>
</div>
<PasswordInput
value={password}
handleChange={(e) => setPassword(e.target.value)}
/>
</div>
</fieldset>
<Button type="submit" theme="registration-button">
Log in
</Button>
<ul>
<li>
<Link to="/register-account">Create account</Link>
</li>
</ul>
</form>
);
};
Fetch request is made here:
userLogin({ email, password }) {
return fetch(`${config.API_ENDPOINT}/auth/login`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({ email, password }),
})
.then((res) => {
!res.ok ? res.json().then((e) => Promise.reject(e)) : res.json();
})
.then((res) => {
TokenService.saveAuthToken(res.authToken);
IdleService.registerIdleTimerResets();
TokenService.queueCallbackBeforeExpiry(() => {
authApiService.postRefreshToken();
});
return res;
});
},
postRefreshToken() {
return fetch(`${config.API_ENDPOINT}/auth/refresh`, {
method: "POST",
headers: {
authorization: `Bearer ${TokenService.getAuthToken()}`,
},
})
.then((res) =>
!res.ok ? res.json().then((e) => Promise.reject(e)) : res.json()
)
.then((res) => {
TokenService.saveAuthToken(res.authToken);
TokenService.queueCallbackBeforeExpiry(() => {
authApiService.postRefreshToken();
});
return res;
})
.catch((err) => {
console.log("refresh token req error");
console.log(err);
});
},
Then on the server this is the route for this request:
authRouter.post("/login", jsonBodyParser, (req, res, next) => {
const { email, password } = req.body;
const userLoggingIn = { email, password };
for (const [key, value] of Object.entries(userLoggingIn))
if (value == null)
return res
.status(400)
.json({ error: `Missing '${key}' in request body` });
authService
.confirmUserNameExists(req.app.get("db"), userLoggingIn.email)
.then((userInDb) => {
if (!userInDb)
return res
.status(400)
.json({ error: "Incorrect email or password" });
});
return authService
.comparePasswords(userLoggingIn.password, userInDb.password)
.then((matchedPw) => {
if (!matchedPw)
return res
.status(400)
.json({ error: "Incorrect email or password" });
const subject = userInDb.email;
const payload = { userId: userInDb.id };
res.send({ authToken: authService.createJwt(subject, payload) });
})
.catch(next);
});
authRouter.post("/refresh", requiresAuthorization, (req, res) => {
const subject = req.user.email;
const payload = { userId: req.user.id };
res.send({
authToken: authService.createJwt(subject, payload),
});
});
Im getting the error in the title (500 error, failed to load resource and its pointing to the fetch request. I've tried googling and looking on stack overflow for hours and can't figure it out. Any help would be greatly appreciated. The goal is just to get the user logged in, redirect the user to the home page, create a JWT and store it.

Update the likes array in a post in the frontend

I have a PUT route in the backend for liking posts, it adds the users id to the likes array in the post. This works fine when tested on Postman (by providing the post in the body) and the likes array is updated. However, when the icon is clicked in the frontend, I want the likes array to update but I'm not sure how to update the state for the post. result is showing the response in the frontend with a 200 status code but that's as far as I'm getting.
How can I update the likes array in the frontend?
Post.js
const Post = (props) => {
const [post, setPost] = useState({});
const [error, setError] = useState(false);
const id = props.match.params.id;
const loadSinglePost = (id) => {
read(id).then((data) => {
if (error) {
console.log(data.error);
setError(data.error);
} else {
setPost(data);
console.log(data);
}
});
};
useEffect(() => {
loadSinglePost(id);
}, [props]);
const like = (id) => {
const {user: { _id }, token} = isAuthenticated();
fetch(`${API}/like/${_id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
id: id,
}),
})
.then(result => { console.log(result)})
.catch((err) => {
console.log(err);
});
};
return (
<div>
<Navbar />
<div>
<h3>{post && post.title}</h3>
<p>
{post && post.author ? post.author.name : ""}
</p>
<p>{post && post.body}</p>
<h5>{post && post.likes && post.likes.length} likes</h5>
<img
onClick={() => {
like(id);
}}
alt="..."
/>
</div>
</div>
);
};
export default Post;
controllers/post.js
exports.like = (req, res) => {
Post.findByIdAndUpdate(req.body._id, {
$push: {likes: req.profile._id}
}, {new: true}).exec((err, result) => {
if (err) {
return res.status(422).json({error: err})
} else {
return res.json(result)
}
})
}
exports.readById = (req, res) => {
const id = req.params.id
Post.findById(id)
.then(post => res.json(post))
.catch(err => res.status(400).json('Error: ' + err));
}
You can update likes in post in then callback like this:
const like = (id) => {
const {user: { _id }, token} = isAuthenticated();
fetch(`${API}/like/${_id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
id: id,
}),
})
.then(result => {
// here update post likes
let updatedPost = {...post}; //to make copy of post
updatedPost.likes = [...updatedPost.likes, id]; //add new id to updatedPost' likes array
setPost(updatedPost); //update post
console.log(result)
})
.catch((err) => {
console.log(err);
});
};
Also from front-end you're sending id key in body:
body: JSON.stringify({
id: id, // here
})
And at back end you're expecting _id
Post.findByIdAndUpdate(req.body._id, { // here
$push: {likes: req.profile._id}
}

Resources