I've been trying to register a user through my vanilla JavaScript front-end, but have been unable to make a POST request that doesn't return a 400 status code. With Postman on the other hand, POST requests work just fine and the user is registered successfully.
This is what is logged when I make POST request:
HTML:
<body>
<form id="signup-form">
<h1>Sign up Form</h1>
<table>
<tr>
<td id="yo">User email: </td>
<td><input type="email" name="email" placeholder="email" /></td>
</tr>
<tr>
<td>Username: </td>
<td><input type="text" name="username" placeholder="username"/></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="password" placeholder="password" /></td>
</tr>
<tr>
<td>Confirm Password:</td>
<td><input type="password" name="password2" placeholder="password" /></td>
</tr>
<tr>
<td><input type="submit" value="signup";/></td>
</tr>
</table>
</form>
<p>Already have an account? Login </p>
<script src="signup.js"></script>
</body>
This is where I need help as to why it returns a 400 response. Front-end JavaScript:
const form = document.getElementById('signup-form');
form.onsubmit = function(e) {
e.preventDefault();
const email = form.email.value;
const username = form.username.value;
const password = form.password.value;
const password2 = form.password2.value;
const user = {
email,
username,
password,
password2,
}
fetch('http://localhost:4002/api/user/register', {
method: 'POST',
body: JSON.stringify(user),
headers:{
'Content-Type': 'application/json'
}
}).then(res => {
console.log(res);
})
.catch(error => console.error('Error:', error));
form.reset();
}
Back-end code incase needed:
Route
//Register user
router.post('/register', (req, res, next) => {
const { errors, isValid } = validateRegisterInput(req.body);
//Check validation
if (!isValid) {
return res
.status(400)
.json(errors);
}
models.User.findOne({
where: {
email: req.body.email
}
})
.then(user => {
if (user) {
errors.email = 'Email already exists';
errors.username = 'Username already exists';
return res
.status(400)
.json(errors)
} else {
const data = {
email: req.body.email,
password: req.body.password,
username: req.body.username,
};
//Encrypting password
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(data.password, salt, (err, hash) => {
if (err)
throw err;
data.password = hash;
models.User.create(data).then(function(newUser, created) {
if (!newUser) {
return next(null, false);
}
if (newUser) {
return next(null, newUser);
}
})
.then( user => {
res.json(user)
})
.catch((err) => {
console.log(err);
})
})
})
}
})
});
Model
"use strict";
module.exports = function(sequelize, DataTypes){
var User = sequelize.define('User', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
username: {
type: DataTypes.STRING,
validate: {
len: [2, 20],
msg: 'Username must be between 2 and 20 characters'
}
},
email: DataTypes.STRING,
password: {
type: DataTypes.STRING,
validate: {
len: {
args: [5],
msg: 'Password must be atleast 5 characters'
}
}
}
});
User.associate = function(models) {
//associations can be defined here
}
return User;
};
Validation
const Validator = require('validator');
const isEmpty = require('./is-empty');
module.exports = function validatorRegisterInput(data) {
let errors = {};
data.username = !isEmpty(data.username)
? data.username
: '';
data.email = !isEmpty(data.email)
? data.email
: '';
data.password = !isEmpty(data.password)
? data.password
: '';
data.password2 = !isEmpty(data.password2)
? data.password2
: '';
if (Validator.isEmpty(data.email)) {
errors.email = 'Email field is required';
}
if (!Validator.isEmail(data.email)) {
errors.email = 'Email field is required';
}
if (Validator.isEmpty(data.password)) {
errors.password = 'Password is required';
}
if (!Validator.isLength(data.password, {
min: 5
})) {
errors.password = 'Password must be atleast 5 characters';
}
return {errors, isValid: isEmpty(errors)}
}
Please try changing the content type to application/json in your fetch call. A form submit would post the entire page back to the server in which case the content type you have would work but you are making a fetch call and preventing the default behavior.
fetch('http://localhost:4002/api/user/register', {
method: 'POST', // or 'PUT'
body: JSON.stringify(user), // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
}).then(res => {
document.getElementById("yo").style.color = "red";
console.log(res);
})
.catch(error => console.error('Error:', error));
Related
Here is the Express route for the signup API call, It works as expected on the server-side but I want to display these same validation messages in real-time when the user will put their credentials on the React client
app.post("/signup", async (req, res) => {
try {
// Validate data before submission
const { error } = registerValidation(
req.body.username,
req.body.firstname,
req.body.lastname,
req.body.email,
req.body.password
);
if (error) return res.status(400).send(error.details[0].message);
// Checking if username already exists
const usernameExist = await database.query(
"SELECT username FROM users WHERE username=$1",
[req.body.username]
);
if (usernameExist.rows[0])
return res.status(400).send("Username already taken!");
// Checking if email already exists
const emailExist = await database.query(
"SELECT email FROM users WHERE email=$1",
[req.body.email]
);
if (emailExist.rows[0])
return res.status(400).send("Email already exists!");
// Hash the password
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(req.body.password, salt);
// Save the New User
const newUser = await database.query(
"INSERT INTO users(username, firstname, lastname, email, password) VALUES($1, $2, $3, $4, $5) RETURNING *",
[
req.body.username,
req.body.firstname,
req.body.lastname,
req.body.email,
hashedPassword,
]
);
res.json(newUser.rows[0]);
} catch (err) {
console.error(err.message);
}
});
And here is my fetch POST request for signup.
export const signUp = async (formData) => {
try {
const res = await fetch(`${API_URL}/signup`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
if (!res.ok) {
const errMsg = await res.text();
throw new Error(errMsg);
}
await res.json();
} catch (err) {
console.error(err.message);
}
};
And here is the signup form using react-hook-form and react-query, the error message is displayed on the console but wanted to display it on screen in real-time while the user typing in the form without the need to submit the form to display those messages.
import React from "react";
import { useForm } from "react-hook-form";
import { joiResolver } from "#hookform/resolvers/joi";
import Joi from "joi";
import { useMutation } from "react-query";
import { useHistory } from "react-router-dom";
import { signUp } from "../API/API";
// Signup Form Validation
const schema = Joi.object({
username: Joi.string().min(3).required(),
firstname: Joi.string().min(3).required(),
lastname: Joi.string().min(3).required(),
email: Joi.string()
.min(6)
.required()
.email({ tlds: { allow: false } }),
password: Joi.string().min(6).required(),
});
const SignupForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ resolver: joiResolver(schema) });
const mutation = useMutation(signUp);
const history = useHistory();
// To submit data on server
const onSubmit = async (data) => {
// const res = await mutation.mutate(data);
// console.log(res);
await mutation.mutate(data);
history.push("/");
};
console.log(errors);
return (
<div>
<h3>Sign up</h3>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="text"
name="username"
placeholder="Username"
{...register("username", { required: true, min: 3 })}
/>
<p>{errors.username?.message}</p>
<br />
<input
type="text"
name="firstname"
placeholder="First Name"
{...register("firstname", { required: true, min: 3 })}
/>
<p>{errors.firstname?.message}</p>
<br />
<input
type="text"
name="lastname"
placeholder="Last Name"
{...register("lastname", { required: true, min: 3 })}
/>
<p>{errors.lastname?.message}</p>
<br />
<input
type="email"
name="email"
placeholder="Email"
{...register("email", {
required: true,
min: 6,
pattern: /^\S+#\S+$/i,
})}
/>
<p>{errors.email?.message}</p>
<br />
<input
type="password"
name="password"
placeholder="Password"
{...register("password", { required: true, min: 6 })}
/>
<p>{errors.password?.message}</p>
<br />
<button type="submit">Sign up</button>
</form>
</div>
);
};
export default SignupForm;
Please check with your syntax and all but did you check onError property?.
const { mutate } = useMutation(signUp, {
onSuccess: () => {
alert("successful");
},
onError: () => {
setStatus("there is an error");
},
});
I'm creating a tinder clone, I can create a user fine however I cant upload a picture. The error I get is ValidationError: User validation failed: pictures: Cast to embedded failed for value "'picture'" at path "pictures". I'm not sure what I'm doing wrong. The post request seems to fire as excepted with the payload however its when I login when I get the error. So I'm sure this has something to do with initial creation of the account.
create account front and back
import React, { useState } from "react";
import {useHistory } from "react-router-dom";
import axios from "axios";
const CreateAccount = () => {
const api = "http://localhost:5000/user/create-account";
const history = useHistory();
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [emailAddress, setEmailAddress] = useState("");
const [password, setPassword] = useState("");
const [gender, setGender] = useState("Male");
const [sexualPreference, setSexualPreference] = useState("Straight");
const [age, setAge] = useState("");
const [description, setDescription] = useState("");
const [picture, setPicture] = useState("");
const account = {
firstName: firstName,
lastName: lastName,
emailAddress: emailAddress,
password: password,
gender: gender,
sexualPreference: sexualPreference,
age: age,
description: description,
pictures: picture
};
console.log(account.gender);
console.log(account.sexualPreference);
console.log(account.pictures)
const submit = () => {
axios
.post(api, account)
.then((res) => {
console.log(res.data);
history.push({
pathname: "/",
});
})
.catch((err) => console.log(err));
};
const handleSubmit = (event) => {
event.preventDefault();
submit();
};
return (
<div>
<div>
<h1>Create account</h1>
</div>
<form onSubmit={handleSubmit} encType="multipart/form-data">
<p>First Name</p>
<input
id="firstName"
name="firstName"
type="firstName"
onChange={(e) => setFirstName(e.target.value)}
></input>
<p>Last Name</p>
<input
id="lastName"
name="lastName"
type="lastName"
onChange={(e) => setLastName(e.target.value)}
></input>
<p>Email Address</p>
<input
id="emailAddress"
name="emailAddress"
type="emailAddress"
onChange={(e) => setEmailAddress(e.target.value)}
></input>
<p>Password</p>
<input
id="password"
name="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
></input>
<p>Gender</p>
<select
id="gender"
name="gender"
type="gender"
onChange={(e) => setGender(e.target.value)}
>
<option value="Male">Male</option>
<option value="Female">Female</option>
</select>
<p>Sexual Preference</p>
<select
id="sexualPreference"
name="sexualPreference"
type="sexualPreference"
onChange={(e) => setSexualPreference(e.target.value)}
>
<option value="Straight" >Straight</option>
<option value="Gay" >Gay</option>
<option value="Lesbian" >Lesbian</option>
<option value="Bisexual" >Bisexual</option>
</select>
<p>Age</p>
<input
id="age"
name="age"
type="age"
onChange={(e) => setAge(e.target.value)}
></input>
<p>Description</p>
<input
id="description"
name="description"
type="description"
onChange={(e) => setDescription(e.target.value)}
></input>
<input
type="file"
name="file"
id="picture"
onChange={(e) => setPicture(e.target.id)}
></input>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default CreateAccount;
router.post( "/user/create-account", [
check("firstName")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "firstName"'),
check("lastName")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "username"'),
check("emailAddress")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "emailAddress"'),
check("password")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "password"'),
check("gender")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "gender"'),
check("sexualPreference")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "sexualPreference"'),
check("age")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "age"'),
check("description")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "description"'),
check("pictures")
.exists({ checkNull: true, checkFalsy: true })
.withMessage('Please provide a value for "pictures"'),
],
asyncHandler(async (req, res, next) => {
// Attempt to get the validation result from the Request object.
const errors = validationResult(req);
// If there are validation errors...
if (!errors.isEmpty()) {
// Use the Array `map()` method to get a list of error messages.
const errorMessages = errors.array().map((error) => error.msg);
// Return the validation errors to the client.
return res.status(400).json({ errors: errorMessages });
}
//new user request body using mongo model from schema
const postUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
emailAddress: req.body.emailAddress,
password: req.body.password,
gender: req.body.gender,
sexualPreference: req.body.sexualPreference,
age: req.body.age,
description: req.body.description,
pictures: req.body.pictures
});
const userEmail = await User.findOne({
emailAddress: postUser.emailAddress,
});
if (postUser.emailAddress === userEmail) {
console.log("User with this email already exists");
return res.status(500).end();
} else if (postUser) {
//if true salts the password with bcryptjs
let salt = await bcryptjs.genSalt(10);
const hashPass = await bcryptjs.hash(postUser.password, salt);
console.log(hashPass);
postUser.password = hashPass;
postUser.save();
res.json({ postUser });
return res.status(201).end();
} else {
res.status(400).send({ error: "Error: Account not created" }).end();
}
})
);
mongoDb Schema
const mongoose = require('mongoose');
const userSchema = mongoose.Schema( {
firstName:{
type: String,
required: true
},
lastName: {
type: String,
require: true
},
emailAddress: {
type: String,
require: true
},
password:{
type: String,
required: true
},
gender:{
type: String,
required: true
},
sexualPreference: {
type: String,
required: true
},
age: {
type: Number,
required: true
},
description: {
type: String,
required: true
},
pictures: {
type: [{
picURL: String,
}],
},
matches: {
type: [{
Object
}],
},
})
module.exports = mongoose.model('User', userSchema);
login backend and frontend
router.post( "/login", asyncHandler(async (req, res, next) => {
const userBody = req.body;
const user = await User.findOne({ emailAddress: req.body.emailAddress });
if (userBody && user) {
console.log(user);
const authenticated = bcryptjs.compare(userBody.password, user.password);
console.log(authenticated);
if (authenticated) {
console.log("match");
const accessToken = jwt.sign(user.toJSON(), process.env.ACCESS_TOKEN_SECRET, { expiresIn: 86400 });
res.cookie("token", accessToken, { httpOnly: false, maxAge: 86400 });
res.setHeader('Authorization', 'Bearer '+ accessToken);
res.json({
user: user,
accessToken: accessToken,
})
.send()
} else {
res.status(403).send({ error: "Login failed: Please try again" }).end();
}
} else {
res.status(403).send({ error: "Login failed: Please try again" }).end();
}
})
);
import React, { useState} from "react";
import { useHistory } from "react-router";
import axios from "axios";
import { Link } from "react-router-dom";
const api = 'http://localhost:5000';
export default function Login () {
const history = useHistory();
const [ email, setEmail ] = useState("");
const [ pass, setPassword ] = useState("");
const submit = () => {
axios.post(`${api}/login`, { emailAddress: email, password: pass }, {withCredentials: true, credentials: 'include'})
.then(res => {
localStorage.setItem('jwt', res.data.accessToken);
history.push({
pathname: `/user/account/${res.data.user._id}`
});
})
.catch(err => console.log(err));
}
const handleSubmit = (event) => {
event.preventDefault();
submit()
}
return (
<div>
<h1>Login</h1>
<form onSubmit={handleSubmit}>
<input
id="emailAddress"
name="emailAddress"
type="text"
placeholder="emailAddress"
onChange={(e) => setEmail(e.target.value)}
/>
<input
id="password"
name="password"
type="password"
placeholder="Password"
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Submit</button>
<button >Cancel</button>
</form>
<p>Don't have a user account?
<Link to="/user/create-account" >Click here</Link>
to sign up!
</p>
</div>
);
}
first of all you can't upload image like this because you send regular http request if you want to send iamge you need to follow this steps
in the frontend you need to send the request with form data for more info read this blog from mdn what is formData mdn you can do something like that with axios append all of the req body to the formData and add it to the axios add multipart/form-data header
axios({
method: "post",
url: "myurl",
data: bodyFormData,
headers: { "Content-Type": "multipart/form-data" },
})
.then(function (response) {
// handle success
})
.catch(function (response) {
//handle error
});
in the server you need to upload-files-or-images-to-server-using-nodejs
i am leraning ejs and variable to add dynamic data in following code is KindofDay and on running server it is giving error KindOfDay is not defined. I want to add data on basis of app.js on to my ejs file but it is producing the error
<body>
<h1>register</h1>
<form action="/api/register" method="POST">
<input type="text" name="email" id="email" placeholder="enter email" /><br />
<input
type="password"
name="password"
id="password"
placeholder="enter password"
/><br />
<button type="submit">register</button>
</form>
<p><%=kindOfDay%></p>
login
<script>
const form = document.querySelector('form')
const email = document.querySelector('#email')
const password = document.querySelector('#password')
form.addEventListener("submit", function(e){
e.preventDefault()
fetch("/api/register", {
// Adding method type
method: "POST",
// Adding body or contents to send
body: JSON.stringify({
email: email.value ,
password: password.value ,
}),
// Adding headers to the request
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
// Converting to JSON
.then(response => response.json())
// Displaying results to console
.then(json => console.log(json));
})
</script>
</body>
this is the code which is rendering the ejs file to update the kindOfDay variable
router
.route("/register")
.get((req, res) => {
res.render('register')
})
.post((req, res) => {
console.log([req.body.password, req.body.email]);
const hashPassword = bcrypt.hashSync(req.body.password, 10);
const checkUser = {
email: req.body.email,
password: req.body.password,
};
const { value, error } = validateUser(checkUser);
if (error) {
status = "error"
res.status(404).render('register' ,{kindOfDay : error.message});
} else {
user = users.find((u) => {
if (u.email === req.body.email) {
return u;
}
});
}
if (user) {
res.status(404).json("userexist");
} else {
const newUser = {
email: req.body.email,
password: hashPassword,
};
users.push(newUser);
res.status(200).json("ok");
}
});
I can signup users just fine on my website, but when I test it with Postman I get this error:
MissingPasswordError: No password given
I also cannot login because the password/username combination is incorrect, so there's definitely something wrong but I can't for the life of me figure out what.
This is my html input field for the password:
<input type="password" class="input--text" name="password" id="password">
signup.js with my fetch function:
const btnSignup = document.querySelector('.btn').addEventListener('click', function () {
let username = document.querySelector('#username').value;
let password = document.querySelector('#password').value;
let bday = document.querySelector('#bday').value;
fetch('http://localhost:3000/users/signup', {
method: "post",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'username': username,
'password': password,
'bday': bday
})
}).then(response => {
return response.json();
}).then(json => {
if (json.status === 'success') {
let feedback = document.querySelector('.alert');
feedback.textContent = "Sign up successful!";
feedback.classList.remove('hidden');
}
})
})
And this is my signup function in my auth.js controller:
const signup = async (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
const bday = req.body.bday;
const user = new Users({
username: username
});
/*user.bday = bday;*/
await user.setPassword(password);
await user.save()
.then(result => {
console.log(result.id);
let token = jwt.sign({
id: result._id
}, "{this is a secret}");
res.json({
'status': 'success',
'data': {
'token': token
}
})
}).catch(error => {
res.json({
'status': 'error'
})
});
}
I've added bodyParser, made sure the name for my input fields are correct, ...
Solved! Turns out my code was correct, but I forgot to set the raw body in Postman to JSON (default was text).
So, I basically wannt to check if the user's Password is the same as the password the user typed into the "oldPassword" input field. The problem I have is how I am suppose to check, cause apparently I have to hash, what the user typed into the input "oldPassword". How can I go about doing this, and please also check my ejs file and what you think? I got new password there as well, THIS IS FOR THE USER TO CHANGE IT'S OLD PASSWORD TO A NEW ONE BY THE WAY*
exports.postChangedPassword = async (req, res) => {
const {
oldPassword,
newPassword,
confirmNewPassword
} = req.body;
try {
const userId = await req.params.userId;
const user = await User.findById(userId)
const oldHashedPassword = await bcrypt.hash(oldPassword, 10);
if (user.password === oldHashedPassword && newPassword === confirmNewPassword) {
const hashedPassword = await bcrypt.hash(newPassword, 10);
user.password = hashedPassword;
user.save();
console.log(user);
res.render("admin/settings/appliedSettings/changed-password", {
pageTitle: "Succesfully Changed Password",
path: "/settings/changed-password",
user: user
})
}
} catch (error) {
console.log(error);
req.flash("error", "Password do not match!");
res.redirect("/settings/password");
}
}
model.js
const mongoose = require("mongoose"),
Schema = mongoose.Schema,
bcrypt = require("bcryptjs");
const postSchema = new Schema({
title: String,
description: String,
context: String,
author: {
type: Schema.Types.ObjectId,
}
});
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
posts: [postSchema]
});
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
next();
});
const Post = mongoose.model("Post", postSchema);
const User = mongoose.model("User", userSchema);
module.exports = {
User,
Post
}
change-password.ejs
<% if (errorMessage) { %>
<div class="user-message-error"> <%= errorMessage %> </div>
<% } %>
<form class="change-password" action="/settings/changed-password/<%=user._id%>" method="POST">
<label for="password">Old Password</label>
<input type="password" name="oldPassword" placeholder="Enter Your Old Password ..." required>
<label for="password">Your New Password</label>
<input type="password" name="newPassword" placeholder="Enter Your New Password ..." required>
<label for="password">Confirm Your New Password</label>
<input type="password" name="confirmNewPassword" placeholder="Confirm Your Password ..." required>
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button type="submit">
Submit Password
</button>
</form>
admin route
router.post("/settings/changed-password/:userId", adminController.postChangedPassword);
To check whether the password changed, just hash the new password and compare the hashed value to the old one:
const oldHashedPassword = await bcrypt.hash(oldPassword, 10);
const newHashedPassword = await bcrypt.hash(newPassword, 10);
if (
user.password === oldHashedPassword &&
newPassword === confirmNewPassword &&
oldHashedPassword !== newHashedPassword
) {
user.password = newHashedPassword;
user.save();
console.log(user);
res.render("admin/settings/appliedSettings/changed-password", {
pageTitle: "Succesfully Changed Password",
path: "/settings/changed-password",
user: user
})
}
It doesn't matter whether you check against user.password or oldHashedPassword, since you established that they are the same thing in the first condition