I'm working on creating a table of users using Bcrypt and keep getting an error:
(node:54133) UnhandledPromiseRejectionWarning: Error: Client was closed and is not queryable
I'm recycling some code from a previous project that is working on that project and not sure where I'm going wrong with it. Below is my test function sitting in my seed.js as well as the function it's calling. As far as I'm seeing it's hitting the helper function but then erroring out, when I console.log my fields in the helper function, I'm getting all the fields, but it's not inserting into my Postgres table.
Seed function
async function testUsers() {
try {
console.log("testing");
bcrypt.hash("bertie99", SALT_COUNT, async function (err, hashedPassword) {
console.log("71", err);
const starter = await createUser({
username: "userone",
password: hashedPassword,
email: "123#yahoo.com",
});
console.log(starter);
});
} catch (error) {
console.log(error);
}
}
Helper function
async function createUser({ username, password, email }) {
try {
console.log("email", email);
console.log("username", username);
console.log("password", password);
const result = await client.query(
`
INSERT INTO users(username, password, email)
VALUES ($1, $2, $3);
`,
[username, password, email]
);
return result;
} catch (error) {
throw error;
}
}
So, it appears that the hashing need an await on it, I edited the function as below and it is working.
try {
console.log("testing");
const hash = await bcrypt.hashSync("bertie99", saltRounds);
const starter = await createUser({
username: "userone",
password: hash,
email: "123#yahoo.com",
});
console.log(starter);
} catch (error) {
console.log(error);
}
}
Related
To simplify the situation I'll just post the following controller for an express route which interactions with a Postgres Database. My question is about error handling. If an error occurs it will be caught within the catch clause. But how can I access the errors thrown by the database queries itself. If I make several await several queries and one of them fails I need probably to restore stuff in the database? For example if the insertion of the user in the user table is a success, but the following query of inserting the user in another table fails, I need to delete the user from the user table again. How does one model such flows?
//
// Register User
//
export const registerUser = async (request, response, next) => {
try {
const usersWithSameMail = await client.query(`SELECT * FROM public.users WHERE email = '${user.email}'`);
if(usersWithSameMail.rows.length > 0){
return response.status(403).json({"code": "ERROR", "message": "Email is already registered"})
} else {
await client.query(`
INSERT INTO public.users(first_name, last_name, email, password)
VALUES ('${user.first_name}', '${user.last_name}', '${user.email}', crypt('${user.password}', gen_salt('bf', 8)));
`);
// more await statements...
return response.status(200).json({"code": "INFO", "message": "Verification mail sent to user"});
}
} catch (error) {
return response.status(500).json({"code": "ERROR", "message": "Error occured while registering the user. Please try again."});
}
}```
You can use middlewares chaining your routes handler. In order to it work, you will have to change your current working code to use Single-responsibility principle. Do only one responsability per middleware and chain all handlers to work as one.
Lets say you want to insert new user, to perform this operation we should:
lookup if email is unique
hash password
Insert new user
return inserted data in postgres back as a response
Following the middleware chaining we should implement a function for each action and chain each action in route definition:
const postgres = require('../../lib/postgres');
const crypto = require('crypto');
exports.insertedData = (req, res) => {
res.status(200).json(req.employee);
};
exports.hashPassword = (req, res, next) => {
crypto.scrypt(req.body.password.toString(), 'salt', 256, (err, derivedKey) => {
if (err) {
return res.status(500).json({ errors: [{ location: req.path, msg: 'Could not hash password'}] });
}
req.body.kdfResult = derivedKey.toString('hex');
next();
});
};
exports.lookupEmailUnique = (req, res, next) => {
const sql = 'SELECT e.email FROM public.users e WHERE e.email=$1';
postgres.query(sql, [req.body.email], (err, result) => {
if (err) {
return res.status(500).json({ errors: [{ location: req.path, msg: 'Could not query database' }] });
}
if (result.rows.length > 0) {
return response.status(403).json({"code": "ERROR", "message": "Email is already registered"})
}
next()
});
}
exports.insertNewUser = (req, res, next) => {
const sql = 'INSERT INTO public.users(first_name, last_name, email, password) VALUES ($1,$2,$3,$4} RETURNING *';
postgres.query(sql, [req.body.first_name, req.body.last_name, req.body.email, req.body.kdfResult], (err, result) => {
if (err) {
return res.status(500).json({ errors: [{ location: req.path, msg: 'Could not query database'}] });
}
req.employee = result.rows[0];
next();
});
};
here is your route declaration:
const router = require('express').Router();
const userService = require('../controllers/user.controller');
router.post('/register', userService.lookupEmailUnique, userService.hashPassword, userService.insertNewUser, userService.insertedData);
module.exports = router;
Here in routes you are using the middeware to do the chaning, you only pass the control to next middleware if all conditions are met and has full control from database erros.
In my example I do not used the async/await but I can change my example to have a version using async/await.
example middleware with transaction
exports.deletePostagem = async (req, res, next) => {
try {
await postgres.query('BEGIN');
const sql2 = 'UPDATE comentario SET postagem = null WHERE postagem = $1';
await postgres.query(sql2, [req.params.id]);
const sql3 = 'DELETE FROM postagem WHERE id = $1';
await postgres.query(sql3, [req.params.id]);
await postgres.query('COMMIT');
res.status(204).json();
res.end();
} catch (err) {
await postgres.query('ROLLBACK');
return res.status(500).json({ errors: [{msg: 'Could not perform operation' }]})
}
}
I used this only as an example, but in my projects I always have a middeware for validate/sanitize the data that comes in request before using in database query prepared statements.
based on transaction documentation in node.js you can use rollback
export const registerUser = async (request, response, next) => {
try {
let error = null;
const client; // create a client, connect to the db
try {
await client.query("begin");
await client.query("first query");
await client.query("second query");
await client.query("third query");
await client.query("commit"); //do commit when is finished all queries
} catch (error) {
error = error;
await client.query("rollback");
} finally {
client.release(); // close the connection
}
if (error) {
return response.status(500).json({ message: error }); // error message
}
return response.status(200).json({ message: "My message" }); // success message
} catch (err) {
return response.status(500).json({ message: err });
}
}
For some reason, only the adminConfirmSignup gives the user pool does not exist error. The CognitoUser doesn't give that error.
Please refer to the code below:
let cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)
var cognitoAdmin = new AWS.CognitoIdentityServiceProvider({ region: process.env.COGNITO_POOL_REGION! });
await cognitoAdmin.adminConfirmSignUp(confirmParams, async(err, data) => { //Only this gives the user pool does not exist error
if (err) {
console.log(`This is the admin user confirm error ---> ${err}`)
} else {
console.log(`Entered else`);
await cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: async(result) => {
cognitoUser.changePassword(resetDetails.currentPassword, resetDetails.newPassword, (err, data) => {
if (err) {
reject(err);
} else {
console.log(`This is the success response of cognito change password -----> ${JSON.stringify(data)}`);
resolve(data);
}
})
},
onFailure: (error) => {
console.log(`This is the onFailure error ----> ${JSON.stringify(error)}`);
reject(error);
}
})
}
})
The password reset works if I use the CognitoUser methods (when I manually confirm the user and use only the cognitoUser methods to authenticate and reset the password).
Your param UserPoolId in SDK calls is incorrect. Won't be able to see that with only what you have posted.
Need another await in front of calling that function
await cognitoUser.changePassword(...)
like this
I'm trying to create a user profile that states that that profile is from one of the business owners in my app. It is supposed to create the profile and then merge info such as the 'roles' array with 'businessOwner' in it and also add the 'businessId'.
Sometimes, the code will work seamlessly. At other times, only the roles and the businessId will be passed to the created user (and all of the other information won't!).
async function writeToFirebase(values) {
authService.createUserWithEmailAndPassword(values.user.email, values.user.senha).then(
async function (user) {
userService.createUserProfileDocument(values.user)
const uid = user.user.uid
const userRef = await userService.doc(uid)
console.log('userRef', userRef)
try {
values.user.uid = uid
const { id } = await businessPendingApprovalService.collection().add(values)
await userRef.set({ roles: ['businessOwner'], businessId: id }, { merge: true })
} catch (error) {
console.error('error merging info')
}
},
function (error) {
var errorCode = error.code
var errorMessage = error.message
console.log(errorCode, errorMessage)
},
)
}
This is createUserWithEmailAndPassword:
async createUserProfileDocument(user, additionalData) {
if (!user) return
const userRef = this.firestore.doc(`users/${user.uid}`)
const snapshot = await userRef.get()
if (!snapshot.exists) {
const { displayName, email, photoURL, providerData } = user
try {
await userRef.set({
displayName,
email,
photoURL,
...additionalData,
providerData: providerData[0].providerId,
})
} catch (error) {
console.error('error creating user: ', error)
}
}
return this.getUserDocument(user.uid)
}
I think that the issue is on this line const snapshot = await userRef.get().
As stated in documentation you should fetch the snapshot using then() function in order to return the promise first.
I think you need to await on the below as well:-
await userService.createUserProfileDocument(values.user)
Since you are setting the user info here(await userRef.set), if you will not wait for the promise, then sometimes, your next block of code(await userRef.set({ roles: ['businessOwner'],) executes and after then your promise might get resolved. Because of this, you might not get the other information sometimes.
You also need to handle the error case of createUserProfileDocument.
I'm trying to work out how to receive helpful error messages on the client side, but keep getting generic error messages. For example, trying to sign up with an email that is not available should result in the email#email.com is already in use error message. I, however, get the generic Request failed with status code 409 message, which is obviously unhelpful to the user. The network response is as expected as seen in the screenshot below. What gives? Why am I not getting the same error message as my (Redux) payload?
Below are the relevant code snippets.
Sign up controller
export default {
signup: async (req, res, next) => {
try {
const { fullname, username, email, password } = req.body;
// Check if there is a user with the same email
const foundUser = await User.findOne({ email });
if (foundUser) {
return res.status(409).send({ error: `${email} is already in use` });
}
const newUser = await User.create({
fullname,
username,
email,
password,
});
// Assign token to succesfully registered user
const token = authToken(newUser);
return res.status(200).send({ token, user: newUser });
} catch (error) {
next(error);
}
},
};
Sign up action
export const createAccount = ({
fullname,
username,
email,
password,
history
}) => async dispatch => {
dispatch({
type: actionTypes.CREATE_ACCOUNT_REQUEST,
});
try {
const {
data: {
newUser: { token, user },
},
} = await request.post('/auth/signup', {
fullname,
username,
email,
password,
});
localStorage.setItem('auth-token', token);
dispatch({
type: actionTypes.CREATE_ACCOUNT_SUCCESS,
payload: user
});
// Redirect to home
history.push('/home');
} catch (error) {
dispatch({
type: actionTypes.CREATE_ACCOUNT_FAILURE,
payload: error.message
});
}
};
Sign up network response
Redux sign up error payload
Try 'error.response.data.error' instead of 'error.message'
I'm building RESTful api with adonisjs. I face this problem in jwt login module. Look at the code below:
async login({request, auth, response}) {
let {email, password} = request.all()
try {
if (await auth.attempt(email, password)) {
let user = await User.findBy('email', email)
let token = await auth.generate(user)
user.password = undefined
Object.assign(user, token)
//------------------------------------------
const assignedToken = await Token.create({
user_id: user.id,
token,
})
// -------- i'd like to catch exception here...
return response.json({
success: true,
user
})
}
} catch(e) {
return response.json({
success: false,
message: 'login_failed'
})
}
}
I'd like to catch possible exception while persisting jwt token to database. I am rather new to adonis. I checked their doc but cannot find exact return type. Do they throw an exception? Or just return null/false? I have no idea. Do you have any?
Do they throw an exception?
Yes
An exception will appear if there is a problem during creation. You can create a new try/catch inside you try/catch. Like:
async login({request, auth, response}) {
...
try {
...
try { // New try/catch
const assignedToken = await Token.create({
user_id: user.id,
token,
})
} catch (error) {
// Your exception
return ...
}
return response.json({
success: true,
user
})
}catch (e) {
...
}
}
It's the simplest solution. I would do it this way because there can be different types of errors.