Jacob Simmons
Jacob Simmons

Reputation: 119

How to use event-driven callbacks with mySQL queries efficiently?

I had some issues with async calls, but after understanding event-driven functions in Node a bit better, I have the following code. It basically does 2 SQL queries. The first checks to see if an existing phone number is in the table. If there is, nothing happens. If there isn't, then it runs a second query to add a new value into the table.

Currently it's event-driven, but .. how do I re-design it so that the outer function query is a separate function, and can even be used as a validation check for code elsewhere? (basically turning it into a generic helper function)?

router.post('/', function(req, res){
    var mysql = req.app.get('mysql');

    //values for 1st query - initial check
    var customerCheck = "SELECT phone_number FROM customer WHERE phone_number=?";
    var phoneNumberCheck = [req.body.phone_number];

    //values for after 1st query validation passes
    var sql = "INSERT INTO customer (name, phone_number, points, is_activated) VALUES (?,?,?,?)";
    var inserts = [req.body.name, req.body.phone_number, 0, true];

    mysql.pool.query(customerCheck, phoneNumberCheck, function(error, results, fields){
        if(error){
            console.log("Failed to verify!");
            res.write(JSON.stringify(error));
            res.end();
        }else{
            console.log(results);
            if(results.length){
                console.log("phone number exists, not adding.");
                res.redirect('/view_customers');
            }else{
                mysql.pool.query(sql,inserts,function(error, results, fields){
                    if(error){
                        console.log("Failed to insert customer to DB!");
                        res.write(JSON.stringify(error));
                        res.end();
                    }else{
                        res.redirect('/view_customers');
                    }
                });

            }
        }
    });
});

Upvotes: 2

Views: 697

Answers (2)

Marcos Casagrande
Marcos Casagrande

Reputation: 40444

Here's a more organized way to do it, of course it can be improved, but I leave that to you.

I added a customer class, where you have check and insert functions, now you can use them individually.

I wrapped the DB query in a promise, to avoid callback hell (Learn how to avoid it here)

Finally the router function has become really simple and easy to read.

customer.js

class Customer {

    constructor(mysql) {
        this.mysql = mysql;
    }

    async check(number) {

        const statement = 'SELECT phone_number FROM customer WHERE phone_number=?';

        const results = await this.query(statement, [number]);

        // If there is a customer with that phone number
        // we return true, false otherwise.
        return results.length;

    }

    insert(name, number) {
        const statement = 'INSERT INTO customer (name, phone_number, points, is_activated) VALUES (?,?,?,?)';

        return this.query(statement, [name, number, 0, true]);

    }

    query(query, placeholders) {
        // We wrap the query in a promise
        // So we can take advantage of async/await in the other functions
        // To make the code easier to read avoiding callback hell
        return new Promise((resolve, reject) => {

            this.mysql.pool.query(query, placeholders, (error, results) => {

                if(error)
                    return reject(error);

                resolve(results);

            });
        });
    }

}

module.exports = Customer;

router

const Customer = require('./customer');

// ... rest of your code

router.post('/', async(req, res) => {

    const customer = new Customer(req.app.get('mysql'));

    try {

        // We check whether the client exists or not
        const check = await customer.check(req.body.phone_number);

        if(!check) // We insert the customer if it doesn't exists
            await customer.insert(req.body.name, req.body.phone_number);

        // After successful insert or if the customer already exists
        // we redirect to /view_customers
        res.redirect('/view_customers');

    } catch(error) {

        // Probably a database error
        console.log('Oops');
        res.write(JSON.stringify(error));
        res.end();
    }

});

Upvotes: 1

k0hamed
k0hamed

Reputation: 502

you can write a function for it that returns a promise to simplify things, something like:

function checkFn(mysql, v) {
  return new Promise((resolve, reject) => {
    var customerCheck = "SELECT phone_number FROM customer WHERE phone_number=?";
    var phoneNumberCheck = [v];
    mysql.pool.query(customerCheck, phoneNumberCheck, function(error, results, fields){
      if(error){
          console.log("Failed to verify!");
          reject();  // reject promise
      }else{
          console.log(results);
          if(results.length){
              console.log("phone number exists, not adding.");
              resolve(true);   // resolve promise with true
          }else{
              resolve(false);   // resolve promise with false
          }
      }
    })
  })
}

then you can use it with async/await

router.post('/', async function(req, res){    // notice: async
  try {
    let numberExist = await checkFn(req.app.get('mysql'), req.body.phone_number)
    if (numberExist) {
       // do something if number exists
    } else {
       // do something if number doesn't exist
    }
  }
  catch (e) {
    // do something if error
  }

read : Async function

Upvotes: 0

Related Questions