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

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.

Related

Why is my promise not resolving correctly?

exports.addUser = async(req, res) => {
const {
username,
email,
password
} = req.body;
//hash password
const password_hash = await hashPassword(password);
//check whitelist
this.checkWhitelist(email).then(function(response) {
if (response) {
console.log("RESOLVED TRUE")
//POST user to Airtable
new Promise(function(resolve, reject) {
return usersTable.create({
email,
username,
password_hash,
"email_verified": "false"
},
function(err) {
if (err) {
resolve(false);
console.error(err);
res.send({
"Success": false,
"responseCode": 502,
})
}
resolve(true);
res.send({
"Success": true,
"responseCode": 200,
});
}
).then(function(response) {
if (response) {
const EMAIL_SECRET = "xxxxxxxxxxx";
jwt.sign({
'username': username,
},
EMAIL_SECRET, {
expiresIn: '1d',
},
(err, emailToken) => {
const url = `http://localhost:3000/confirmation/${emailToken}`;
transporter.sendMail({
to: args.email,
subject: 'Confirm Email',
html: `Please click this email to confirm your email: ${url}`,
});
}
)
}
})
})
} else {
console.log('RESOLVED FALSE')
res.send({
"Success": false,
"responseCode": 403
})
}
})
}
For some reason, the promise I created at usersTable.create is not resolving correctly. When I call .then() after, I get the error: UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'then' of undefined.
For context, this is the user registration flow for a webapp. First, the pass is hashed, then the email is check against a whitelist (so far this logic is working). Now I just need to verify the email, but can't get the .then() to call correctly.
What's going on?
In your first then where you return createTable you'll need to return new Promise so it can be chained to the next then.
If createTable returns a promise, you can simply write return createTable and get rid of the new Promise that wraps it.
Since you are using async-await earlier in the code, I'd recommend you moving to that completely as it makes the code much easier to read.
I took a stab at that,
exports.addUser = async(req, res) => {
const {
username,
email,
password
} = req.body;
//hash password
const password_hash = await hashPassword(password);
//check whitelist
try {
const whitelist = await this.checkWhiteList(email)
if (whitelist) {
await usersTable.create() // shortened for readability sake.
const EMAIL_SECRET = 'xxxxxxxxxxx';
jwt.sign(
{
'username': username,
},
EMAIL_SECRET,
{
expiresIn: '1d',
},
(err, emailToken) => {
const url = `http://localhost:3000/confirmation/${emailToken}`;
transporter.sendMail({
to: args.email,
subject: 'Confirm Email',
html: `Please click this email to confirm your email: ${url}`,
});
}
);
}
res.send({
'Success': true,
'responseCode': 200,
});
} catch (error) {
res.send({
'Success': false,
'responseCode': 403,
});
}
}

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

Problem with ERR_HTTP_HEADERS_SENT and warning a promise

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 });
});

Review my API Controller

I am new to javascript backend and I am currently learning to build a RESTful API using node.js, express.js, sequelize.js with MySQL as my database. I have successfully built a basic Tasks API as a test. I am looking for feedback on if I did this correctly as far as javascript best practices go within one controller. Any feedback will be appreciated.
Current Logic: User can own multiple tasks
Everything works fine. I am authenticating the users using JWT strategy in Passport.js. I am authenticating them at the router level and then I am double-checking their records in db through userid before they are allowed to make any updates or deletes to their own records. Anyways, here is the controller:
'use strict';
var jwt = require('jsonwebtoken');
var config = require('../config'),
db = require('../services/database'),
Task = require('../models/task');
var TaskController = {};
// GET ALL Tasks
TaskController.get = function (req, res) {
if (!req.user.id) {
res.json({ message: 'You are not authorized.' });
} else {
db.sync().then(function () {
return Task.findAll({ where: { userid: req.user.id } }).then(function (result) {
res.status(202).json(result);
});
});
}
}
// POST ONE Task
TaskController.post = function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to post.' });
} else {
db.sync().then(function () {
var newTask = {
userid: req.user.id,
task: req.body.task
};
return Task.create(newTask).then(function () {
res.status(201).json({ message: 'Task Created!' });
});
});
}
}
// PUT ONE Task
TaskController.put = function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to update.' });
} else {
db.sync().then(function () {
// Find task by task id and user id
Task.find({ where: { id: req.params.id, userid: req.user.id } })
.then(function (task) {
// Check if record exists in db
if (task) {
task.update({
task: req.body.task
}).then(function () {
res.status(201).json({ message: 'Task updated.' });
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
});
});
}
}
// DELETE ONE Task
TaskController.delete = function (req, res) {
if (!req.params.id) {
res.json({ message: 'Please provide a task to delete.' });
} else {
db.sync().then(function () {
Task.find({ where: { id: req.params.id, userid: req.user.id } })
.then(function (task) {
if (task) {
task.destroy({ where: { id: req.params.id } })
.then(function () {
res.status(202).json({ message: 'Task deleted.' });
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
});
});
}
}
module.exports = TaskController;
The TaskController.js looks good but I would suggest moving all the ORM logic (Sequelize) to a file called TaskService.js
Example -
In TaskService.js -
...
exports.delete = function() {
db.sync().then(function () {
Task.find({ where: { id: req.params.id, userid: req.user.id } })
.then(function (task) {
if (task) {
task.destroy({ where: { id: req.params.id } })
.then(function () {
res.status(202).json({ message: 'Task deleted.' });
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
});
});
}
then in TaskController.js -
...
const TaskService = require('./TaskService);
...
TaskController.delete = function(req, res) {
if (!req.params.id) {
res.json({ message: 'Please provide a task to delete.' });
} else {
TaskService.delete();
}
}
One thing I'd like to call out as far as Javascript best practices would be nested promises, which is a bit of an anti-pattern. You lose the power of promise chains when you nest them, in effect creating nested callbacks. Things will start getting weird once you start trying to use .catch() blocks for error handling. A quick refactor with catch blocks might look like this, even though this is still messy because of the conditional based on the whether or not the task exists in the DB:
// PUT ONE Task
TaskController.put = function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to update.' });
} else {
db.sync()
.then(function () {
// Find task by task id and user id
// NOTE: we return the promise here so that we can chain it
// to the main promise chain started by `db.sync()`
return Task.find({ where: { id: req.params.id, userid: req.user.id } });
})
.then(function (task) {
// Check if record exists in db
if (task) {
task.update({ task: req.body.task })
.then(function () {
res.status(201).json({ message: 'Task updated.' });
})
.catch(function (updateError) {
// do something with your update error
// catches an error thrown by `task.update()`
});
} else {
res.status(404).json({ message: 'Task not found.' });
}
})
.catch(function (promiseChainError) {
// do something with your promiseChainError
// this catch block catches an error thrown by
// `db.sync()` and `Task.find()`
});
}
}
Alternatively, if you're more comfortable with synchronous style code and have the option of updating your node version to v7+, this is what your functions might look like using async/await:
// PUT ONE Task
TaskController.put = async function (req, res) {
if (!req.body.task) {
res.json({ message: 'Please provide a task to update.' });
} else {
try {
await db.sync();
const task = await Task.find({ where: { id: req.params.id, userid: req.user.id } });
// Check if record exists in db
if (task) {
await task.update({ task: req.body.task });
res.status(201).json({ message: 'Task updated.' });
} else {
res.status(404).json({ message: 'Task not found.' });
}
} catch (error) {
// do some something with your error
// catches all errors thrown by anything in the try block
}
}
}
There's plenty of resources out there about promise chains and handling async methods. Check this one out in particular: http://solutionoptimist.com/2013/12/27/javascript-promise-chains-2/

Categories

Resources