Problem with ERR_HTTP_HEADERS_SENT and warning a promise - javascript

I have two warnings about the code below and I do not know how to solve these problems.
First problems is warning: Warning: a promise was created in a handler and problem is after commented my code line.
and secound error is:
Unhandled rejection Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client. I commented code with this line
// Login Action
exports.login = (req, res) => {
function getTodayDate() {
const today = new Date();
return today;
}
function getToken(userId) {
const token = jwt.sign(
{
id: userId,
},
env.SECRET_KEY,
{
expiresIn: '60min',
},
);
return token;
}
User.findOne({
where: {
login: req.body.login,
},
})
.then(isUser => {
if (isUser) {
if (bcrypt.compareSync(req.body.password, isUser.password)) {
User.update( // <- this is started my problem?
{
last_present_logged: getTodayDate(),
},
{ where: { login: req.body.login } },
).then(() =>
res.status(200).json({
success: true,
token: getToken(isUser.id),
}),
);
}
User.update(
{
last_failed_logged: getTodayDate(),
},
{ where: { login: req.body.login } },
).then(() => {
res.status(200).json({ // <- this is my red error!
error: 'Auth failed. The password is incorrect.',
success: false,
});
});
} else {
res
.status(200)
.json({ error: 'Auth failed. User does not exist', success: false });
}
})
.catch(() => {
/* just ignore */
});
};
how can I solve these problems?
how can I solve these problems?

The problem is that you're ending the request twice if bcrypt.compareSync() is truthy. If you do that, you get Headers already sent
What you have to do is either return inside the if, or wrap the next User.update inside an else
User.findOne({
where: {
login: req.body.login,
},
})
.then(isUser => {
if (isUser) {
if (bcrypt.compareSync(req.body.password, isUser.password)) {
return User.update( // <- this is started my problem?
{
last_present_logged: getTodayDate(),
},
{ where: { login: req.body.login } },
).then(() =>
res.status(200).json({
success: true,
token: getToken(isUser.id),
}),
);
}
// Now this code won't be executed if `compareSync` is truthy.
// Issue a return, so in case of failure, it goes to the .catch
// avoiding UnhandledPromiseRejection warning
return User.update(
{
last_failed_logged: getTodayDate(),
},
{ where: { login: req.body.login } },
).then(() => {
res.status(200).json({ // <- this is my red error!
error: 'Auth failed. The password is incorrect.',
success: false,
});
});
} else {
res
.status(200)
.json({ error: 'Auth failed. User does not exist', success: false });
}
})
.catch(() => {
/* just ignore */
// Please, don't ignore, end the request here
res.status(500).json({ error: 'Internal server error });
});

Related

Cannot set headers after they are sent to the client when not fond the record i nodejs

i want find record and update the the record in mongoose and nodejs .
async DeleteRole(req, res, next) {
let validationData = await this.ValidationAction(req, res);
if (validationData[0]) {
Role.findByIdAndUpdate(
req.params.id,
{ $set: { isDelete: true } },
{ useFindAndModify: false },
(error, role)=> {
console.log(role)
if (error) next(error);
if (!role) return res.status(200).send({
message: "NotFoundRecord",
statusCode: 200,
success: false,
});
}
);
return res.status(200).send({
message: "Success",
statusCode: 200,
success: true,
});
}
return res.status(200).send({
message: "BadREquest",
statusCode: 400,
success: false,
});
}
i want when record not found it show me the errro the record not found and somve one else .
but when i send a request it show me this error :
F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\helpers\promiseOrCallback.js:19
throw error;
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:533:11)
at ServerResponse.header (F:\Projects\Nodejs\SalesSignal\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (F:\Projects\Nodejs\SalesSignal\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (F:\Projects\Nodejs\SalesSignal\node_modules\express\lib\response.js:267:15)
at ServerResponse.send (F:\Projects\Nodejs\SalesSignal\node_modules\express\lib\response.js:158:21)
at RoleController.Notfound (F:\Projects\Nodejs\SalesSignal\src\http\controller\BaseController.js:26:28)
at F:\Projects\Nodejs\SalesSignal\src\http\controller\RoleController.js:25:34
at F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\model.js:4882:16
at F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\model.js:4882:16
at F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\model.js:4882:16
at F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\helpers\promiseOrCallback.js:16:11
at F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\model.js:4905:21
at F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\query.js:4382:18
at model.Query.Query._findAndModify (F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\query.js:3462:12)
at model.Query. (F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\query.js:3037:8)
at model.Query._wrappedThunk [as _findOneAndUpdate] (F:\Projects\Nodejs\SalesSignal\node_modules\mongoose\lib\helpers\query\wrapThunk.js:16:8)
Emitted 'error' event on Function instance at:
**now i want tow things : A => find the best way for find and update record and solve this problem ? how can i solve this problem ??? **
You're missing an else statement or a return at the next(error) call, and also the code that sends the Success message doesn't wait for the findByIdAndUpdate to finish - it always sends the response before anything else.
async DeleteRole(req, res, next) {
let validationData = await this.ValidationAction(req, res);
if (validationData[0]) {
Role.findByIdAndUpdate(req.params.id, {
$set: { isDelete: true }
}, { useFindAndModify: false }, (error, role)=> {
if (error) {
next(error);
} else if (!role) {
res.status(200).send({
message: "NotFoundRecord",
statusCode: 200,
success: false,
});
} else {
res.status(200).send({
message: "Success",
statusCode: 200,
success: true,
})
}
});
} else {
res.status(200).send({
message: "BadREquest",
statusCode: 400,
success: false,
});
}
}

Object is returned the same although I'm mutating it

The upcoming code snippet is removing the password attribute from the user JSON object and return it in response. what is happening is that the password attribute is still returning!
const signin = (req, res, next) => {
let requestBody = req.body;
userModel.findUserByEmail(requestBody.email).then(user => {
bcrypt.compare(requestBody.password, user.password, (error, result) => {
if (!result) {
return res.status(500).json({
status: false,
message: 'Auth Failed!',
error
});
}
if (error) {
return res.status(500).json({
error
});
}
let token = jwt.sign({
email: user.email,
userId: user._id
},
process.env.JWT_KEY,
{
expiresIn: "2h"
});
// remonve password key
delete user.password
res.status(200).json({
status: true,
message: 'Authenticated!',
data: {
token,
user
}
});
});
}).catch(error => {
return res.status(500).json({
status: false,
message: 'Auth Failed!',
error
});
});
}
not sure the problem is related to async compilation or not
You could create a new object without the password and use that in your response:
const { password, ...restOfUser } = user
res.status(200).json({
status: true,
message: 'Authenticated!',
data: {
token
user: restOfUser
}
})

How can I reorganize my return in an async function?

let's say I've got a function where I'm fetching for some data from a DB.
findById(id) {
return Model.findById(id)
}
I need to reorganize the return from the user data like this:
{
name: "Tom",
age: 57
}
into something like this:
{
message: "User is found successfully",
success: true,
user: user
}
So far I can manage with that with a Promise "then" section, like this:
return Model.findById(id)
.then(user => {
if (!user) {
logger.warn(`Coundn't find user with id: ${id}`);
return { message: `Coundn't find user with id: ${id}`, success: false, user: null };
}
return { message: `User with id: ${id}`, success: true, user: user };
})
.catch(err => {
if (err) {
logger.error(err.message);
return { message: err.message, success: false, user: null };
}
})
Can I do the same with a async/await and return my reorninazed return?
Because so far it returns the user object from the DB:
async findById(id) {
return await this.model.findById(id, function (user, err) {
if (err) {
console.log('test');
logger.error(err);
return { message: err.message, success: false, user: null };
}
if (!user) {
logger.warn(`Coundn't find user with id: ${id}`);
return { message: `Coundn't find user with id: ${id}`, success: false, user: null };
}
return { message: `User with id: ${id}`, success: true, user: user };
});
}
Thanks in advance!
Most database APIs do NOT support both a callback and a promise at the same time. If you pass a callback, they do not return a promise. Pick one style or the other. Your first approach using .then() works just fine as that is all promise-based.
Your second approach does not work because you're passing a regular callback. That tells the database to NOT return a promise because you're using the older callback style, but you're trying to use that promise.
If you want to use async/await, you could do so like this:
async findById(id) {
try {
let user = await this.model.findById(id);
if (user) {
return { message: `User with id: ${id}`, success: true, user: user };
} else {
logger.warn(`Coundn't find user with id: ${id}`);
return { message: `Coundn't find user with id: ${id}`, success: false, user: null };
}
} catch(e) {
logger.error(err);
return { message: err.message, success: false, user: null };
}
}
FYI, you can remove the if (err) test in the .catch() handler from your first code block. If .catch() is triggered, there is an error - you don't need to test if one is there.

Handling errors in Express.js in service / controller layers

I am writing an application in Express.js with a separate controller layer and a service layer. Here is my current code:
user.service.js
exports.registerUser = async function (email, password) {
const hash = await bcrypt.hash(password, 10);
const countUser = await User.countDocuments({email: email});
if(countUser > 0) {
throw ({ status: 409, code: 'USER_ALREADY_EXISTS', message: 'This e-mail address is already taken.' });
}
const user = new User({
email: email,
password: hash
});
return await user.save();
};
exports.loginUser = async function (email, password) {
const user = await User.findOne({ email: email });
const countUser = await User.countDocuments({email: email});
if(countUser === 0) {
throw ({ status: 404, code: 'USER_NOT_EXISTS', message: 'E-mail address does not exist.' });
}
const validPassword = await bcrypt.compare(password, user.password);
if (validPassword) {
const token = jwt.sign({ email: user.email, userId: user._id }, process.env.JWT_KEY, { expiresIn: "10s" });
return {
token: token,
expiresIn: 3600,
userId: user._id
}
} else {
throw ({ status: 401, code: 'LOGIN_INVALID', message: 'Invalid authentication credentials.' });
}
};
user.controller.js
exports.userRegister = async function (req, res, next) {
try {
const user = await UserService.registerUser(req.body.email, req.body.password);
res.status(201).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
exports.userLogin = async function (req, res, next) {
try {
const user = await UserService.loginUser(req.body.email, req.body.password);
res.status(200).json({ data: user });
} catch (e) {
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
}
}
The code works, but requires some corrections. I have a problem with error handling. I want to handle only some errors. If another error has occurred, the 500 Internal Server Error will be returned.
1) Can I use "throw" object from the service layer? Is this a good practice?
2) How to avoid duplication of this code in each controller:
if(!e.status) {
res.status(500).json( { error: { code: 'UNKNOWN_ERROR', message: 'An unknown error occurred.' } });
} else {
res.status(e.status).json( { error: { code: e.code, message: e.message } });
}
3) Does the code require other corrections? I'm just learning Node.js and I want to write the rest of the application well.
Yes, you can throw errors from service layer, it is good practice to catch errors with try/catch block in controller
I handle this with a custom error middleware, just use a next function in a catch block.
catch (e) {
next(e)
}
Example of error middleware (for more info check docs, fill free to move a middleware to file)
app.use(function (err, req, res, next) {
// err is error from next(e) function
// you can do all error processing here, logging, parsing error messages, etc...
res.status(500).send('Something broke!')
})
From my point of view it looks good. If you looking for some best practice and tools, try eslint (with AirBnb config for example) for linting, dotenv for a environment variables management, also check Node.js Best Practice
i want to give you an example:
this code in your controller
findCar(idCar)
} catch (error) {
switch (error.message) {
case ErrorConstants.ELEMENT_NOT_FOUND('LISTING'): {
return {
response: {
message: ErrorMessages.ELEMENT_NOT_FOUND_MESSAGE('LISTING'),
},
statusCode,
}
}
default: {
return {
response: {
message: ErrorMessages.UNKNOWN_ERROR_MESSAGE,
},
statusCode,
}
}
}
}
and this code in your service
findCar: async listingId => {
try {
if (some condition) {
throw new Error(ErrorConstants.ELEMENT_NOT_FOUND('LISTING'))
}
return { ... }
} catch (error) {
console.error(error.message)
throw new Error(ErrorConstants.UNKNOWN_ERROR)
}
},
controller is going to catch the service's errors

How can I repair my code? Warning: a promise was created

For some time now I have such a mistake in the console and I do not really understand what is going on and how I should fix it. Error:
can someone show me how to write it better?
Warning: a promise was created in a handler at /Users/pietrzakadrian/Desktop/bankApplicationOfficial/server/controllers/user.controller.js:119:16 but was not returned from it, see
at Function.Promise.attempt.Promise.try (/Users/pietrzakadrian/Desktop/bankApplicationOfficial/node_modules/bluebird/js/release/method.js:29:9)
and this is my code and I added a comment to the line where there is supposed to be a mistake?
// Login Action
exports.login = (req, res) => {
function getTodayDate() {
const today = new Date();
return today;
}
function getToken(userId) {
const token = jwt.sign(
{
id: userId,
},
env.SECRET_KEY,
{
expiresIn: '60min',
},
);
return token;
}
User.findOne({
where: {
login: req.body.login,
},
})
.then(isUser => {
if (isUser) {
if (bcrypt.compareSync(req.body.password, isUser.password)) {
User.update( // <- this
{
last_present_logged: getTodayDate(),
},
{ where: { login: req.body.login } },
).then(() => {
res.status(200).json({
success: true,
token: getToken(isUser.id),
});
});
} else {
User.update(
{
last_failed_logged: getTodayDate(),
},
{ where: { login: req.body.login } },
).then(() => {
res.status(200).json({
error: 'Auth failed. The password is incorrect.',
success: false,
});
});
}
} else {
res
.status(200)
.json({ error: 'Auth failed. User does not exist', success: false });
}
})
.catch(() => {
/* just ignore */
});
};
Bluebird's documentation (check out the examples at : Warning explanations section says :
This usually means that you simply forgot a return statement somewhere, which will cause a runaway promise that is not connected to any promise chain.

Categories

Resources