using .then nodejs - javascript

I have a large function that says, okay add this employee to the db based on what the admin entered. But now I need to check, does this user already exist in the db. so I created a function that does just that called getEmployeeNum, but I need to perform a .then in the main function of /addEmployee to say, see what the result of the function getEmployeeNum is before you perform any of the other requests. see the code below:
app.post('/addEmployee', (req, res) => {
if (req.session.loggedin) {
var firstname = req.body.firstname;
var lastname = req.body.lastname;
var username = req.body.username;
var sales = req.body.sales;
var salary = req.body.salary;
var location = req.body.location;
var role = req.body.role;
var admin = req.body.admin;
var employeenum = req.body.employee_num;
var phonenum = req.body.phone_num;
var org = req.body.org;
var pass = "";
var newPassword = req.body.password
getEmployeeNum(req, res, employeenum)
bcrypt.hash(newPassword, saltRounds, function(err, hash) {
pass = hash
addLogin(req, res, pass, firstname, lastname, username, sales, salary, location, role, admin, employeenum, phonenum)
});
var addEmployee = "insert into EMPLOYEES (FIRSTNAME, LASTNAME, USERNAME, SALES, SALARY, LOCATION, ROLE, ADMIN, EMPLOYEE_NUM, PHONENUM, ORGANIZATION) VALUES ('" +
req.body.firstname +
"', '" +
req.body.lastname +
"', '" +
req.body.username +
"', '" +
req.body.sales +
"', '" +
req.body.salary +
"', '" +
req.body.location +
"', '" +
req.body.role +
"', '" +
req.body.admin +
"', '" +
req.body.employee_num +
"', '" +
phonenum +
"', '" +
org +
"' )";
ibmdb.open(ibmdbconnMaster, function(err, conn) {
if (err) return console.log(err);
conn.query(addEmployee, function(err, rows) {
if (err) {
console.log(err);
}
registerEmail(username, firstname, lastname, req, res)
res.redirect('/employees')
})
})
} else {
res.render('login.ejs')
}
})
function getEmployeeNum(req, res, employeenum) {
var getEmployeeNum = "select * from employees"
ibmdb.open(ibmdbconnMaster, function(err, conn) {
if (err) return console.log(err);
conn.query(getEmployeeNum, function(err, rows) {
if (err) {
console.log(err);
}
for (var i = 0; i < rows.length; i++) {
var employee_num = rows[i]["EMPLOYEE_NUM"]
if (employeenum == employee_num) {
alert("employee already exists")
res.render("addEmployee.ejs")
}
}
conn.close(function() {
// console.log("closed the function /index");
});
});
})
}
is this the right way to do it, or is there a better way? Thanks :)

I see that you're using the callback version of SQL driver. I'll assume that you;re working with mysql2 for simplicity wise.
There is actually a promise version of mysql2 driver
const mysql = require('mysql2/promise');
I'll share with you some of the common patterns I use when working with DB.
// Create a pool for connection
const pool = mysql.createPool({
connectionLimit: process.env.SQL_CON_LIMIT,
host: process.env.SQL_SERVER,
port: process.env.SQL_PORT,
user: process.env.SQL_USERNAME,
password: process.env.SQL_PASSWORD,
database: process.env.SQL_SCHEME,
timezone: process.env.SQL_TIMEZONE,
});
// To use async to test the connection via conn.ping() before launch server
const p1 = (async () => {
const conn = await pool.getConnection();
await conn.ping();
conn.release();
return true;
})();
// test connection for SQL, add other into array as you like
Promise.all([p1])
.then(() => {
app.listen(PORT, () =>
console.info(
`Application started on port http://localhost:${PORT}/ at ${new Date()}`
)
);
})
.catch((err) => {
console.error('Cannot connect: ', err);
});
The code block above is for setting up, and test connection before starting the server. This can avoid the rare case where DB is not initialized before the request came in (As the server can start before connecting to DB)
const makeQuery = (query, pool) => {
return async (args) => {
const conn = await pool.getConnection();
try {
let results = await conn.query(query, args || []);
return results[0]; // Result of query is in index 0
} catch (error) {
console.log(error);
} finally {
conn.release();
}
};
};
// Sample query
const queryCheckLogin =
'SELECT COUNT(*) as "match" FROM user WHERE user_id = ? AND password = ?';
// Make it into function!
const checkLogin = makeQuery(queryCheckLogin, pool);
app.post('/api/login', async (req, res) => {
let { user_id, password } = req.body;
// Obtain sha1 password from submitted password
password = sha1(password);
try {
let results = await checkLogin([user_id, password]);
// Return the credential (supposedly token) when record is matched
if (results[0]['match'] !== 0) {
res.status(200).json({ login: 'success', user_id, password });
} else {
// return 401 if record not found
res.status(401).json({ error: 'No such username or password' });
}
} catch (error) {
console.log(error);
res.status(400).json(error);
}
});
The code block above shows the factory function to deal with the general form of getting the result from a query, so you won't clutter the logic in middleware. So you will write out whatever query you will do, make it into function via makeQuery, and just use the resulting function.
Using async...await will also make the code cleaner, however, this depends on the version of codebase you're working on. However, the sample above do works for .then as well.

Related

Postgres query is not executed on Node.js

I've got into an issue. Postgres code(query) is not executed at all. I have to mention that the connection with the DB is correctly done.
I've tried to debug the code but I don't really know what's the problem
'use strict';
const config = require('../config');
const emailService = require('./email-service');
const pg = require('pg');
pg.defaults.ssl = true;
module.exports = function(phone_number, user_name, previous_job, years_of_experience, job_vacancy){
console.log('sending email');
let emailContent = 'A new job inquiry from ' + user_name + ' for the job: ' + job_vacancy +
'.<br> Previous job position: ' + previous_job + '.' +
'.<br> Years of experience: ' + years_of_experience + '.' +
'.<br> Phone number: ' + phone_number + '.';
emailService.sendEmail('New job application', emailContent);
try{
var pool = new pg.Pool(config.PG_CONFIG);
pool.connect(function(err, client, done) {
if (err) {
return console.error('Error acquiring client', err.stack);
}
client
.query(
'INSERT into job_applications ' +
'(phone_number, user_name, previous_job, years_of_experience, job_vacancy) ' +
'VALUES($1, $2, $3, $4, $5) RETURNING id',
[phone_number, user_name, previous_job, years_of_experience, job_vacancy],
function(err, result) {
if (err) {
console.log(err);
} else {
console.log('row inserted with id: ' + result.rows[0].id);
}
});
});
}
catch(e){
console.log(e, 'error[][][]')
}
pool.end();
} ```

I'm getting "Cannot set headers after they are sent to the client"

As I understood when I send the response the register function doesn't end ! I tried to put return with res and still have the same problem !
const connection = require("../../db");
const bcrypt = require("bcrypt");
const register = (req, res) => {
const { user_name, password, phone, email } = req.body;
const role_id = 0;
var dateObj = new Date();
const month = dateObj.getUTCMonth() + 1;
const day = dateObj.getUTCDate();
const year = dateObj.getUTCFullYear();
const created_at = year + "/" + month + "/" + day;
const is_disabled = 0
// Cheacking if there is no email or user name in the database.
let query = `SELECT * from users WHERE email = '${email}' OR user_name ='${user_name}' `;
connection.query(query, (err, result) => {
if (err) throw err;
if (result.length) {
if (email === result[0].email) {
return res.json("Email is already used.")
} else {
return res.json("User name is already used.")
};
};
});
//Hashing the password.
const hashPassword = bcrypt.hashSync(password, Number(process.env.SALT), (err, result) => {
if (err) throw err
});
// Adding new user to database.
query = `insert INTO users (email,phone,password,user_name,role_id,created_at,is_disabled) VALUES('${email}','${phone}','${hashPassword}','${user_name}','${role_id}','${created_at}','${is_disabled}')`
connection.query(query, (err, result) => {
if (err) throw err
})
return res.json("Added user")
}
module.exports = register
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
You should move the password hashing/new user code inside the callback since these must happen only after the email/username checks are already done. Otherwise, you'll asynchronously be checking for conflicts and creating the account; potentially resulting in trying to send multiple responses back to the client.
const register = (req, res) => {
const { user_name, password, phone, email } = req.body;
const role_id = 0;
var dateObj = new Date();
const month = dateObj.getUTCMonth() + 1;
const day = dateObj.getUTCDate();
const year = dateObj.getUTCFullYear();
const created_at = year + "/" + month + "/" + day;
const is_disabled = 0
// Cheacking if there is no email or user name in the database.
let query = `SELECT * from users WHERE email = '${email}' OR user_name ='${user_name}' `;
connection.query(query, (err, result) => {
if (err) throw err;
if (result.length) {
if (email === result[0].email) {
return res.json("Email is already used.")
} else {
return res.json("User name is already used.")
};
};
//Hashing the password.
const hashPassword = bcrypt.hashSync(password, Number(process.env.SALT), (err, result) => {
if (err) throw err
});
// Adding new user to database.
query = `insert INTO users (email,phone,password,user_name,role_id,created_at,is_disabled) VALUES('${email}','${phone}','${hashPassword}','${user_name}','${role_id}','${created_at}','${is_disabled}')`
connection.query(query, (err, result) => {
if (err) throw err
})
return res.json("Added user")
});
}

How do I correctly await the return of an async function in javascript?

I'm currently working on a Google Sign-in Auth app with a React frontend and an Express backend and I'm currently stuck in the part of the process where I'm validating tokens on the backend. The docs for this process show this code to validate the token:
const {OAuth2Client} = require('google-auth-library');
...
const client = new OAuth2Client(CLIENT_ID);
async function verify() {
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
});
const payload = ticket.getPayload();
const userid = payload['sub'];
// If request specified a G Suite domain:
//const domain = payload['hd'];
}
verify().catch(console.error);
I've implemented this code in my own project here:
//verify token
async function verify(token, client) {
const ticket = await client.verifyIdToken({
idToken: token,
audience: keys.google.clientID,
});
const payload = ticket.getPayload();
const userid = payload['sub'];
const domain = payload['hd'];
const email = payload['email']
console.log('User ID: ' + userid);
console.log('Domian: ' + domain);
console.log('Email: ' + email);
var message = '';
var cookie = {};
await User.find({email: email}, (error, user) => {
if(error) {
message = error;
} else if (user.length === 0) {
message = 'this user is not in the database';
} else {
message = 'this user is in the database';
const session = new Session({
email: email,
session_token: token
});
cookie = {
email: email,
session_token: token
};
session.save((error, session) => {
if (error) {
console.log(error);
} else {
console.log('session saved');
}
});
console.log(message);
}
});
return Promise.resolve(cookie);
}
//recieve token id from frontend, verify it, and send session back in response
router.post('/google', (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
let cookie = verify(body, client).catch(console.error);
console.log('Cookie:' + cookie);
return res.send(cookie);
});
Currently when this runs everything inside the async function executes, but the return statement only returns the empty promise object. I think I'm making a mistake using async and await incorrectly, but I don't know how to correctly get the function to wait for all the work verifying the token and then update the DB before returning.
Not sure if this will help, but when I call the route my console gives me this output:
(I took out my personal info from the output fields, but assume these lines actually have gmail account info)
...
Cookie:[object Promise]
User ID: <GOOGLE ID>
Domian: <DOMAIN>
Email: <USER EMAIL>
this user is in the database
session saved
Thanks for reading!
Since "verify" function is an async function, you should add "await" before calling it. For catching errors you can simply place it in a try/catch:
router.post('/google', async (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
try {
let cookie = await verify(body, client);
console.log('Cookie:' + cookie);
return res.send(cookie);
} catch(error) {
// handling error
console.log(error);
return res.send("error")
}
});
`
You're mixing async/await with callback based calls. I don't know the internals of the library you're using, but the pattern should look more like this:
var cookie = {};
try{
const user = await User.find({email: email});
if (user.length === 0) {
console.log('this user is not in the database');
}
else {
console.log('this user is in the database');
const session = new Session({
email: email,
session_token: token
});
try{
await session.save();
console.log('session saved');
} catch(err){
console.log(err);
}
return {
email: email,
session_token: token
};
} catch(error){
console.log(error);
}

Getting single message from Graph

I'm trying to get a single email from an Office 365 Mailbox.
I'm sending the email id to my app via a POST (req.body.id) and then calling this code in order to get some email properties:
router.post('/id', async function(req, res, next) {
console.log("email with ID -> ", req.body.id)
let parms = { title: 'Inbox', active: { inbox: true } };
const accessToken = await authHelper.getAccessToken(req.cookies, res);
const userName = req.cookies.graph_user_name;
if (accessToken && userName) {
parms.user = userName;
// Initialize Graph client
const client = graph.Client.init({
authProvider: (done) => {
done(null, accessToken);
}
});
try {
const result = await client
.api('/me/messages/', req.body.id)
.select('id,subject,from,toRecipients,ccRecipients,body,sentDateTime,receivedDateTime')
.get();
parms.messages = result.value;
console.log("email -> ", result.value);
res.render('message', parms);
} catch (err) {
parms.message = 'Error retrieving messages';
parms.error = { status: `${err.code}: ${err.message}` };
parms.debug = JSON.stringify(err.body, null, 2);
res.render('error', parms);
}
} else {
// Redirect to home
res.redirect('/');
}
});
At the moment, result.value contains all of the messages in the mailbox instead of just the message with provided id.
Could someone tell me where my error is, please?
The api method has a single path parameter. Calling it like .api('/me/messages/', req.body.id) is effectivly sending it a path ("/me/messages/") along with an additional parameter it ignores.
You need to send it a single string so you'll need to append the req.body.id to the path ({path} + {id}):
const result = await client
.api('/me/messages/' + req.body.id)
.select('id,subject,from,toRecipients,ccRecipients,body,sentDateTime,receivedDateTime')
.get();

Node js returning values from function

I live in the PHP world but I am attempting to build out a REST Api using Node.
I have been stuck all day of trying to return an array of results from a for loop. Basically I am passing an array of field_name:field_value. I want to push the result from the update into an array to return. I can get it to log in the console but no further.
Here is a sample post json data
{
"first_name":"Jeff",
"phone":"4855555555"
}
Here is the function and loop
function UpdateKey(user_id, key, value, cb) {
connection.query('UPDATE users SET ' + key + ' = ? WHERE id = ? LIMIT 1', [value, user_id], function(err, results) {
if (err) {
callback = key + " update failed.";
} else {
callback = key + " was updated.";
}
cb(callback);
});
}
for (myKey in post_data) {
UpdateKey(user_id, myKey, post_data[myKey], function(id) {
console.log(id);
});
}
res.send(JSON.stringify({ "status": 200, "error": "", "response": my_results_here }));
I have been researching async but not sure the best route here. Any help would be great!
You could collect all results in an array and send that when the arrays size equals the keys size:
const keys = Object.keys(post_data);
const response = [];
for(const myKey of keys) {
UpdateKey(user_id, myKey, post_data[myKey], function(id) {
response.push(id);
if(keys.length === response.length) {
res.send(JSON.stringify({
status: 200,
error: "",
response
}));
}
});
}
The solution You want:
const updateUserField = (userId, field, value) => {
return Promise((resolve) => {
const query = 'UPDATE users SET ' + field + ' = ? WHERE id = ?';
const data = [value, userId];
connection.query(query, data, (error) => {
if (error) return resolve(field + ' update failed');
resolve(field + ' was updated');
});
});
};
router.post('/user/:id', async (req, res) => {
const userId = req.params.id;
const data = req.body;
const response = [];
for (const field in data) {
response.push(
await updateUserField(userId, field, data[field])
);
}
res.status(200).send({
response
});
});
or in parallel:
router.post('/user/:id', async (req, res) => {
const userId = req.params.id;
const data = req.body;
const response = await Promise.all(
Object
.keys(data)
.map(field => updateUserField(userId, field, data[field]))
);
res.status(200).send({
response
});
});
Correct solution
As I understand You want to get post data and update record in users table.
So why not just do it in one query?
Try this way:
const updateUser = (userId, data) => {
return Promise((resolve, reject) => {
const query = 'UPDATE users SET ? WHERE id = ?';
connection.query(query, [data, userId], (error) => {
if (error) return reject(error);
resolve();
});
});
};
router.post('/user/:id', async (req, res) => {
try {
const userId = req.params.id;
const data = req.body;
await updateUser(userId, data);
res.status(200).send({
message: 'User account successfully updated'
})
}
catch (error) {
console.error(error);
res.status(500).send({
message: 'Failed update user account'
});
}
});
But better think about using ORM i.e. Sequelize for security, validation and etc features that eases dev's life.

Categories

Resources