I have cretaed a simple signup functionality and added some checks to it which aren't working correctly.
They are giving the error :
(node:14256) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:558:11)
at ServerResponse.header (C:\Users\hp\Desktop\Projects\chatapp-backend\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (C:\Users\hp\Desktop\Projects\chatapp-backend\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\hp\Desktop\Projects\chatapp-backend\node_modules\express\lib\response.js:267:15)
at CreateUser (C:\Users\hp\Desktop\Projects\chatapp-backend\controllers\auth.js:29:58)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:14256) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:14256) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
According to my expectation while using postman when i enter the same email or username it should give message accordingly but it gives such error
auth.js file:
const Joi=require('joi');
const HttpStatus=require('http-status-codes');
const bcrypt=require('bcryptjs');
const User=require('../models/userModels');
const Helpers=require('../Helpers/helpers');
module.exports={
async CreateUser(req,res){
const schema = Joi.object({
username: Joi.string()
.min(5)
.max(10)
.required(),
email: Joi.string()
.email()
.required(),
password: Joi.string()
.min(5)
.required()
});
const validation = schema.validate(req.body);
res.send(validation);
console.log(validation);
const userEmail=await User.findOne({email :(req.body.email)});
if(userEmail){
return res.status(HttpStatus.StatusCodes.CONFLICT).json({message: 'Email already exist'});
}
const userName=await User.findOne({
username: (req.body.username)
});
if(userName){
return res.status(HttpStatus.StatusCodes.CONFLICT).json({message: 'Username already exist'});
}
return bcrypt.hash(req.body.password,10,(err,hash)=>{
if(err){
return res.status(HttpStatus.StatusCodes.BAD_REQUEST).json({message: 'Error hashing password'});
}
const body={
username: Helpers.firstUpper(req.body.username),
email: Helpers.lowerCase(req.body.email),
password: hash
}
User.create(body).then((user) => {
res.status(HttpStatus/HttpStatus.StatusCodes.CREATED).json({message: 'USer created successfully',user})
}).catch(err => {
res.status(HttpStatus.StatusCodes.INTERNAL_SERVER_ERROR).json({message: 'Error occured'})
})
});
}
};
helpers.js file
const User = require('../models/userModels');
module.exports={
firstUpper: username => {
const name = username.toLowerCase();
return name.charAt(0).toUpperCase() + name.slice(1);
},
lowerCase: str => {
return str.toLowerCase();
},
};
This is a popular error - "Error: Can't set headers after they are sent."
If you are sending data after you have already sent a final request like res.send(), you will see this.
//Don't send anything to your response after this.
res.send(validation);
//You have multiple res.status after this, and if any of that runs then you will see this error.
You are trying to send response twice while first is already in transition. I'm not sure why you want to send back the validation to the client but this is causing an issue :
const validation = schema.validate(req.body);
// return res.send(validation); // or just add this validation to another response
console.log(validation);
try {
const userEmail=await User.findOne({email :(req.body.email)});
if(userEmail){
return res.status(HttpStatus.StatusCodes.CONFLICT).json({message: 'Email already exist', validation: validation});
}
const userName=await User.findOne({
username: (req.body.username)
});
if(userName){
return res.status(HttpStatus.StatusCodes.CONFLICT).json({message: 'Username already exist', validation: validation});
}
}
catch(console.errror);
Also, when you use async/await use try/catch for error handling. You should check this post to get why this happens and how to avoid it in future.
Related
I have a back-end using firebase-admin and express to allow post requests from the client to the server to make changes to the firestore I have that contains stuff like user data (this is a test and not a real product). I want to check if a document already exists so a user cannot register with that username again. I have first seen instances of doc.exists but that returns undefined for me and I looked into the documentation and found doc.empty which is said to check if a document is empty. I tried it but it returned a promise rejection error. If I changed that line to .exists or to something else, that goes away so I have narrowed down the issue to that line.
index.js (backend)
app.post("/registeruser", function (req, res) {
res.setHeader("Content-Type", "application/json");
try {
const username = req.body.username;
const password = req.body.password;
const passwordEncrypted = HmacSHA1(password, JSON.parse(fs.readFileSync("./keys.json"))["passwordEncryptKey"]).toString();
// console.log(username, password, passwordEncrypted);
try {
firestore.collection("users").get(username).then(function (data) {
if (data.empty == false) {
throw [true, "Already registered user!"];
}
}).catch(function (error) {
throw [true, error];
});
if (username == "") {
firestore.collection("users").add({
username: v4(),
passwordhash: passwordEncrypted,
email: "example#gmail.com",
}).then(function () {
return res.status(200).send(JSON.stringify({
error: false,
message: "Successfully registered user!",
}))
}).catch(function (error) {
throw [true, error];
});
}
else {
firestore.collection("users").doc(username).set({
username: username,
passwordhash: passwordEncrypted,
email: "example#gmail.com",
}).then(function () {
return res.status(200).send(JSON.stringify({
error: false,
message: "Successfully registered user!",
}));
}).catch(function (error) {
throw [true, error];
});
}
}
catch (error) {
throw [true, error];
}
}
catch (error) {
console.log(error);
const [isError, errorMessage] = error;
return res.status(404).send(JSON.stringify({
error: isError,
message: errorMessage,
}));
}
});
Terminal Output
(node:29448) UnhandledPromiseRejectionWarning: [object Array]
(node:29448) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:29448) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
You have multiple concurrent promise chains, and some of those can fail independently. You need to consolidate all your logic into one promise chain.
return firestore.collection("users").get(username)
.then((data) => {
if (data.empty == false) {
throw [true, "Already registered user!"];
}
})
.then(() => {
if (username == '') {
return firestore.collection("users").add({/* Your data */});
}
return firestore.collection("users").doc(username).set({/* Your data */});
})
.then(() => {
return res.status(200);
})
.catch((err) => {
return res.status(500);
});
You can also try using async/await which will significantly simplify logic like this.
i am trying to give only name in the body and want error in the postman ...but for the status response in postman is 201 created but it is throwing error in console as
UnhandledPromiseRejectionWarning: ValidationError: User validation failed: password: Path password is required., email: Path email is required.
at model.Document.invalidate (C:\projects\MERN\backend\node_modules\mongoose\lib\document.js:2564:32)
at C:\projects\MERN\backend\node_modules\mongoose\lib\document.js:2386:17
at C:\projects\MERN\backend\node_modules\mongoose\lib\schematype.js:1181:9
at processTicksAndRejections (internal/process/task_queues.js:79:11)
(node:6524) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:6524) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
why there is no error in postman???????????
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
name:{
type : String,
required : true
},
email:{
type : String,
required : true,
unique:true,
},
password:{
type : String,
required : true,
minlength: 7
},
date:{
type :Date,
default: Date.now
}
})
const User = mongoose.model('User',userSchema)
module.exports = User
router.post("/", async (req, res) => {
try {
const user = await new User(req.body);
user.save();
res.status(201).send({user});
} catch (e) {
res.status(500).send(e);
}
});
consider that your node application throws some error right and crashes as you describe well above. Because your node app is interfacing with the internet you need to devise a way to interpret the error from you app into to an error that is known by the internet also, that way postman will be able to tell that an error has occured...So how do we achieve this, the answer is error handling...
We will use your User model as you have described, and consider the code below it...
router.post("/", async (req, res) => {
try {
const user = await new User(req.body);
// One important thing to note is that the return of this function call below is
// a Promise object which means that it executes asynchrounously and from the error
// log you have above, it is the reason your app is crashing...
user.save();
res.status(201).send({user});
} catch (e) {
res.status(500).send(e);
}
});
So then lets fix it...
router.post("/", async (req, res) => {
const user = await new User(req.body);
return user.save()
// the then call simply accepts a callback that is executed after the async is complete
.then((result) => res.status(201).send({user}))
// this catch will be called in case the call encounters an error during execution
.catch((error) => res.status(500).send(error));
});
Note now we handle the error in the catch by responding to the HTTP request as you have with a 500 code...and also sending the error along with the response
I'm doing a RESTful API for a vuejs frontend with authentication and data saving by account.
I get this error when trying to log a user in.
Basically my frontend will send a request with a body containing a username and password.
Here's the log in middleware in express :
exports.post_LogIn = (req, res, next) => {
User.findOne({ username: req.body.username })
.then(foundUser => {
if (!foundUser) {
console.log("no user");
const error = new Error("No user with that username");
error.statusCode = 400;
error.tosend = "No user with that username";
throw error;
} else {
return bcrypt.compare(req.body.password, foundUser.password);
}
})
.then(isEqual => {
if (!isEqual) {
console.log("wrong password");
const error = new Error("Passwords don't match");
error.statusCode = 400;
error.tosend = "Passwords don't match";
throw error;
}
console.log("logged in");
const token = jwt.sign(
{
username: foundUser.username
},
"secretpassword",
{ expiresIn: "24h" }
);
console.log(token);
res.status(200).json({ username: foundUser.username });
})
.catch(err => {
res.status(err.statusCode).json({ message: err.tosend });
});
};
So basically when there's a problem with the credentials (either wrong username or password) I wanna send a response to the front end with a message saying what was wrong, and let the front end deal with it.
This works just fine and as expected.
However when I send valid credentials, my console.log statement goes through, but the token and response part gives me this error
(node:14515) UnhandledPromiseRejectionWarning: RangeError
[ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: undefined
at ServerResponse.writeHead (_http_server.js:242:11)
at ServerResponse._implicitHeader (_http_server.js:233:8)
at write_ (_http_outgoing.js:579:9)
at ServerResponse.end (_http_outgoing.js:689:5)
at ServerResponse.send (/home/martin/dev/projets/trader-backend/node_modules/express/lib/response.js:221:10)
at ServerResponse.json (/home/martin/dev/projets/trader-backend/node_modules/express/lib/response.js:267:15)
at /home/martin/dev/projets/trader-backend/controllers/UserController.js:97:34
(node:14515) UnhandledPromiseRejectionWarning: Unhandled promise
rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 1) (node:14515) [DEP0018]
DeprecationWarning: Unhandled promise rejections are deprecated. In
the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
I tried commenting out the token part, or commenting the response and console logging the token, and I still get the same error.
However if I comment out the token and use res.send instead of res.json it seems to be working fine and my front end gets the response.
Any idea what might be causing this ? I'm kind of lost here
my user.js
const express=require('express')
const router=express.Router()
const gravatar=require('gravatar')
const bcrypt=require('bcryptjs')
const {check,validationResult} =require('express-validator/check')
const User=require('../../models/User')
//#route POST api/users
//#desc Register user
//#access public
router.post('/',[
check('name','Name is required').not().isEmpty(),
check('email','please include a valid email').isEmail(),
check('password','please enter password with more than 6 characters').isLength({min:6})
],async(req,res)=>{
const errors=validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({errors:errors.array()})
}
const {name,email,password}=req.body;
try{
//see if the user exists
let user = await User.findOne({ email: email })
//if record exists in DB
if (user) {
return res.status(400).json({ errors: [{ msg: "user already exists" }] });
}
//get users gravatar
const avatar=gravatar.url(email,{
s:'200', //size
r:'pg', //rating of image
d:'mm' //gives a default image
})
user=new User({
name,
email,
avatar,
password
})
//encrpyt password
const salt=await bcrypt.genSalt(10) // 10-no of rounds.more the better
user.password=await bcrypt.hash(password,salt); //coverts to hash pass
await user.save();
//return jsonwebtoken
res.send('User registered')
}catch(e){
console.log(e.message)
res.status(500).send('server error')
}
res.send('User route')
})
module.exports=router;
the app is working fine but in the terminal i get
(node:1022) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:464:11)
at ServerResponse.header (/Users/udayshetty/Desktop/MERN app/node_modules/express/lib/response.js:771:10)
at ServerResponse.send (/Users/udayshetty/Desktop/MERN app/node_modules/express/lib/response.js:170:12)
at /Users/udayshetty/Desktop/MERN app/routes/api/users.js:60:9
at processTicksAndRejections (internal/process/task_queues.js:85:5)
(node:1022) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:1022) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
This error occurs when your app is responding to the client over HTTP - and you try to send the response / response headers twice. Once the response is sent, the connection will be closed. You can only send them once for a given http request. You can keep a connection open and send more data , but you cannot send the response headers again.
Please try to remove res.send('User route').
You are sending a response twice.
I am using axios, express validator and bcryptjs for the function of letting a user change their password in their account area. The field I am checking against is the one where the user needs to type in their existing password. It then hashes their input and checks it against the database hashed password. When doing other validation I am able to get the error message response back via axios but for this will not:
body("currentPass")
.custom((value, { req }) => {
return User.findOne({ _id: req.user }).then(userDoc => {
bcrypt.compare(value, userDoc.password).then(domatch => {
if (!domatch) {
return Promise.reject("no match");
}
});
});
})
The error I get is:
UnhandledPromiseRejectionWarning: no match Unhandled promise
rejection. This error originated either by throwing inside of an async
function without a catch block, or by rejecting a promise which was
not handled with .catch(). (rejection id: 3)
If I add a catch block then when validation fails the catch block fires instead of the Promise.reject
body("currentPass")
.custom((value, { req }) => {
return User.findOne({ _id: req.user })
.then(userDoc => {
bcrypt.compare(value, userDoc.password)
.then(domatch => {
if (!domatch) {
return Promise.reject("no match");
}
})
.catch(err => {
console.log('catch block error');
})
});
})