How to display errors with Axios in React - node.js

Good day,
i am running to a slight problem with Axios, i want to check if the status code is ok, if not then i send the error to the client. How can i do that ?
This is my attempt to do it, but i had no success:
Route code:
router.post("/register", async (req, res) => {
try {
//check if fields are empty
const { name, email, password, rePassword } = req.body;
if (!name || !email || !password || !rePassword) {
return res.status(406).send("fields are empty");
}
//passsword length should be at leats 6 characters
if (password.length < 6) {
return res
.status(406)
.send("Password should at least be 6 characters long");
}
//checking if passwords match
if (password !== rePassword) {
return res.status(406).send("Passwords dont match");
}
//check if user exists
const emailExist = await User.findOne({ email: req.body.email });
if (emailExist) {
return res.status(406).send("User already exists");
} else {
//create salt
const salt = await genSalt(10);
//hashing the password
const hashedPassword = await hash(req.body.password, salt);
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashedPassword,
isAdmin: req.body.isAdmin,
});
const newUser = await user.save();
res.status(200).send(newUser);
}
} catch (err) {
console.log(err);
}
});
Page code:
const handleSubmit = async e => {
e.preventDefault();
axios
.post("http://localhost:5000/users/register", {
name,
email,
password,
rePassword,
})
.then(response => {
console.log(response.data);
})
.catch(err => {
console.log(err);
});
}

The response will give you the following options
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
This will allow you to check for valid stausText/codes, or display errors accordingly.

Related

validator isStrongPassword is not a function

I get this error whenever I try to register a user!
isStrongPassword is not a function
I tried typing the isStrongPassword which is Min 8 characters, 1 uppercase, 1 lowercase, 1 number, 1 symbol but no luck, I don't know what i'm missing !!
Auth.js
const isEmail = require ("validator/lib/isEmail.js")
const isStrongPassword = ("validator/lib/isStrongPassword");
//REGISTER
router.post("/register", async (req, res, next) => {
try {
if (!req.body.username) {
return next(createError(400, "Invalid username"));
} if (!req.body.email || !isEmail(req.body.email)) {
return next(createError(400, "Invalid email"));
} if (!req.body.password) {
return next(createError(400, "Invalid password"));
} else if (!isStrongPassword(req.body.password)) {
return next(
createError(
400,
"Invalid password: Min 8 characters, 1 uppercase, 1 lowercase, 1 number, 1 symbol"
)
);
} else {
const checkUsername = await User.findOne({ username: req.body.username });
const checkEmail = await User.findOne({ email: req.body.email });
if (checkUsername) {
return next(createError(400, "Username taken"));
} else if (checkEmail) {
return next(createError(400, "Email taken"));
} else {
//generate new password
const salt = await bcrypt.genSalt(10);
const hashedPass = await bcrypt.hash(req.body.password, salt);
//create new user
const newUser = new User ({
username: req.body.username,
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: hashedPass,
repeatPassword: hashedPass,
birthday: req.body.birthday,
});
//save user and respond
const user = await newUser.save();
res.status(200).json({
message: "Successfully registered",
user,
});
}
}
} catch(err) {
next(err);
}
});
You are missing a require statement from your isStrongPassword import - and also presumably the file extension. e.g.
const isStrongPassword = ("validator/lib/isStrongPassword");
Should presumably be
const isStrongPassword = require ("validator/lib/isStrongPassword.js");

How to determine return value is an error when using MVC?

I was trying to separate the logic of my back-end server into route/controller/service,
but one problem is when I try to catch error in service layer, I will return error, but how can I determine if it's error and return 404 status in my controller layer ?
Here's the code
Service layer
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return savedUser
} catch (err){
return err
}
};
Controller layer
const register = (req,res) => {
const { name, email, password } = req.body
const user = UserService.register(name,email,password);
if(how to determine here?){
res.status(201).json(user);
}
}
You could return an object in your Service Layer instead of an User or Error object:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return { user: savedUser, error: null }
} catch (error){
return { error, user: null }
}
};
In your Controller Layer you would then do something like this:
const register = (req,res) => {
const { name, email, password } = req.body
const { user, error } = UserService.register(name,email,password);
if(error){
res.status(500).json({ user, error: error.message });
} else {
res.status(200).json({ user, error })
}
}
return the save promise and handle errors in your controller
serivce:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
return newUser.save();
};
controller:
const register = async (req,res) => {
const { name, email, password } = req.body
try {
const user = await UserService.register(name,email,password);
res.status(201).json(user);
} catch(error) {
// handle error here
}
}

Node.js bcrypt compare problem only return false

I want to do my login API with Node.js and MongoDB and when I compare my pass from input to the one that's in the db I always get false I read other post on StackOverFlow but didn't helped me.
I think I also found the problem: When I use hash on my password input to check it manually with the one from db at every request is onatherone.
So maybe that's the problem but I don't know how to solve. I read a lot about this but still cant solve here is my code:
const match = await bcrypt.compare(password, user.password, (res) => {console.log(res)}) //false
my login api
router.post('/login', body('email').isEmail(), body('password').exists(), async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() })
}
const { email, password } = req.body
const user = await User.findOne({ email })
if (!user) {
return res.status(401).json({ "err": "invalid credentials" })
}
// const match = await bcrypt.compare(password, user.password).then(function (res) { console.log(res) })
const match = await bcrypt.compare(password, user.password);
console.log(match)
})
and here is the register api
router.post("/register", body("email").isEmail(), body("password").isLength({ min: 5 }), body("username").isLength({ min: 1 }), async (req, res) => {
const { email, username, password } = req.body
const errors = validationResult(req);
const saltRounds = 10;
try {
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() })
}
//check if the user exist
const duplicated = await User.findOne({ email })
if (duplicated) { return res.status(401).json({ "err": "Email is taken" }) }
const user = new User({ username, email, password })
//crypt pass
user.password = bcrypt.hashSync(process.env.secret, saltRounds);
//generate token with jwt
const payload = {
id: user.id
}
jwt.sign({
payload,
}, process.env.jwtSecret, { expiresIn: '999h' }, (err, token) => { console.log(token) });
//save the user
await user.save()
res.status(200).send("User Stored")
} catch (error) {
res.status(500).send(error.body)
}
})
Your problem is that you using bcrypt.hash in the wrong way.
It seems like you provide this method kind of key, which you shall not provide.
This method accepts the value to hash, and saltRounds.
So you basically need to change your Registration API code to:
router.post("/register", body("email").isEmail(), body("password").isLength({ min: 5 }), body("username").isLength({ min: 1 }), async (req, res) => {
const { email, username, password } = req.body
const errors = validationResult(req);
const saltRounds = 10;
try {
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() })
}
//check if the user exist
const duplicated = await User.findOne({ email })
if (duplicated) { return res.status(401).json({ "err": "Email is taken" }) }
const user = new User({ username, email, password })
//crypt pass
user.password = bcrypt.hashSync(password, saltRounds);
//generate token with jwt
const payload = {
id: user.id
}
jwt.sign({
payload,
}, process.env.jwtSecret, { expiresIn: '999h' }, (err, token) => { console.log(token) });
//save the user
await user.save()
res.status(200).send("User Stored")
} catch (error) {
res.status(500).send(error.body)
}
}
And more specific, you store the password with:
user.password = bcrypt.hashSync(password, saltRounds);
try use Promise for this
const match = await new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, function(error, res){
if (error) { reject(error); }
resolve(res);
})
})

ExpressJS - Unhandled rejection Error: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I am able to register and login to the application but I receive the following server error:
"Unhandled rejection Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" upon registration. I came across similar questions here but none of them resolved my problem.
authController.js:
const User = require("../models/User");
const jwt = require("jsonwebtoken");
const simplecrypt = require("simplecrypt");
const sc = simplecrypt();
process.env.SECRET_KEY = "secret";
exports.postLogin = (req, res, next) => {
const { username, password } = req.body;
let validationMessages = [];
if (!username || !password) {
validationMessages.push({ message: "Please fill in all fields" });
}
if (password.length < 6) {
validationMessages.push({
message: "Password should be at least 6 characters"
});
}
if (validationMessages.length > 0) {
res.sendStatus(403).json(validationMessages);
} else {
User.findOne({ where: { username: username } })
.then(user => {
if (!user) {
res.sendStatus(400).json({
message: "Invalid username or password"
});
} else if (password == sc.decrypt(user.password)) {
const token = jwt.sign(user.dataValues, process.env.SECRET_KEY, {
expiresIn: 1440 // expires in 24 hours
});
res.send(token);
}
})
.catch(err => {
res.send("Error: " + err);
});
}
};
exports.postRegister = (req, res, next) => {
const { username, password, password2 } = req.body;
let validationMessages = [];
//Check required fields
if (!username || !password || !password2) {
validationMessages.push({ message: "Please fill in all fields" });
}
if (password.length < 6 || password2.length < 6) {
validationMessages.push({
message: "Password should be at least 6 characters"
});
}
if (password !== password2) {
validationMessages.push({
message: "Passwords do not match"
});
}
if (validationMessages.length > 0) {
return res.sendStatus(400).json(validationMessages);
} else {
User.findOne({ where: { username: username } })
.then(user => {
if (user) {
return res.sendStatus(403).json("User already exists");
}
const hashedPassword = sc.encrypt(password);
User.create({ username: username, password: hashedPassword })
.then(user => {
return res.sendStatus(200).send(user);
})
.catch(err => {
throw new Error(err);
});
})
.catch(err => {
throw new Error(err);
});
}
};
exports.getProfile = (req, res, next) => {
const decoded = jwt.verify(
req.headers["authorization"],
process.env.SECRET_KEY
);
User.findOne({
where: {
id: decoded.id
}
})
.then(user => {
if (user) {
res.statusCode(200).json(user);
} else {
throw new Error("User does not exist");
}
})
.catch(err => {
throw new Error(err);
});
};
I am using Node.JS v12.14.0 and Express.JS v4.17.1.
I resolved it myself. My problem was using res.sendStatus which sets the given response HTTP status code and sends its string representation as the response body. res.json will set the content-type response header, but at time time the response will already have been sent to the client. So simply res.send() should replace res.sendStatus().

Update a property in document in an Express route (Mongoose, MongoDB, Express)

I've successfully set up the registration and login functionality using Express, MongoDB and Mongoose.
I would like to log when the user last visited the site once the user's credential is accepted in a lastConnection property of the user document,
I tried but "lastConnection" is null (see the line below where I add a comment)
router.post("/login", async function(req, res) {
const { errors, isValid } = validateLoginInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
const user = await User.findOne({ email }).then(user => {
if (!user) {
errors.email = "Email already exists";
}
console.log("user ", user); <-- returns an object with the datas of user
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
const payload = {
id: user.id,
name: user.name
};
user.lastConnection = new Date(); <-- doesn't work
jwt.sign(
payload,
keys.secretOrKey,
{
expiresIn: 7200
},
(err, token) => {
res.json({
success: true,
token: "Bearer " + token
});
}
);
} else {
errors.password = "Password is not correct";
// return res
// .status(400)
// .json({ passwordincorrect: "Password incorrect" });
}
});
});
return {
errors,
isValid: isEmpty(errors)
};
});
Any ideas? I think I have to do an update but I don't know where to put it
Try replacing user.lastConnection = new Date(); with
user.update({ lastConnection: new Date() })
.then( updatedUser => {
console.log(updatedUser)
// put jwt.sign code here
})

Resources