0

I am trying to implement a use case where users can request to check his password by providing username. Issue: Node js back-end app returns result before query is complete. I have checked articles explaining async aspect of javascript. However, I am still unsure if the solutions can be applied when there is a rest call involved in between.

High level flow:

  1. Front-end js calls rest api(node.js application) with username to get password
  2. Node app calls DB to get password
  3. Returned password is displayed on front end

Code:

Front-end:

function getPassword() {//Called by clicking button on ui
var username = 'testuser'; //hardcoding for simplicity
  $.ajax({
    url: urlWhereNodeJsAppIsHosted,
    error: function(password) {
      console.log('error' + JSON.stringify(password));
    }, success: function(data) {
      console.log('success' + JSON.stringify(password)); // Displaying on console for now
    }
  });
}

Back-end(Node js app):

const express = require('express');
const request = require('request');
const mysql = require('mysql');
const app = express();

var connection = mysql.createConnection({
    host: 'localhost',
    port: '3306',
    user: 'dbuser',
    password: 'dbuserpassword',
    database: 'dbname'
});

connection.connect(function(err) {
  if (err) {console.log(err);}
});

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  next();
});

app.get('/verify', (req, res) => {
  var username = req.query.username;
  var password = 'noresult';
  connection.query(
    'SELECT * FROM userpassword where user=?, ${[username]}', 
    function(err, rows, fields) {
      password = extractpasswordfromrows(rows);//iterate and get result
    }
  );
  console.log(connection.threadId); //Value is not undefined
  res.send(200, { success: password });
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => console.log(`listening on ${PORT}`));

Database:

userpassword(user, password) table with existing data.

I am new to javascript as well as thinking asynchronously. Please help with your inputs. Thanks.

hbc1234
  • 13
  • 1

1 Answers1

0

The code sends password outside the success callback without waiting for the query to complete. Try

app.get('/verify', (req, res) => {
var username = req.query.username;
var password = 'noresult';
connection.query(
 'SELECT * FROM userpassword where user=?, ${[username]}', 
 function(err, rows, fields) {
   password = extractpasswordfromrows(rows);//iterate and get result
   res.send(200, { success: password }); // Ok, have password here
   }
 );
 // Not Ok, don't have password here
 console.log(connection.threadId); //Value is not undefined
});

I haven't attempted to put in check code to verify that the err parameter in the call back is not truthy, even if logically seen as impossible.

For general treatment of coding against this error, see "How do I return the result from an asynchronous call".

Processing results outside the callback

To avoid nesting code, callback code can resolve or reject a newly created promise depending on processing results, with result and error handling functions attached to the promise using its then/catch methods.

Alternatively in Express you can supply multiple callbacks (middleware functions) to app.get and use next calls to advance to the next middleware function if and when required. As an example based on the post (compiled but not tested):

app.get('/verify', 

  (req, res, next) => {
    var username = req.query.username;
    connection.query(
      'SELECT * FROM userpassword where user=?, ${[username]}', 
       function(err, rows, fields) {
         if( err) {
           res.status(500).send("no result");
         }
         else {
           res.locals.rows = rows;
           next();
         }
       }
     );
  },

  (req,res) => {
     const password = extractpasswordfromrows(res.locals.rows); //iterate and get result
     res.send(200, { success: password });
  }
);
traktor
  • 17,588
  • 4
  • 32
  • 53
  • Thanks for your input. I was able to get the result if I moved the res.send inside callback as suggested. If I keep at both places(inside callback and at original place as in problem), I get initialization value of password('noresult'). Can you help explain why res/result is not sent back immediately if its being updated from async function. I thought once any async function is called(connect.query here), code execution will move to subsequent lines and as here we have no further lines it will exit from app.get(/verify, ...) call with empty res. – hbc1234 Dec 21 '20 at 19:03
  • If it helps, `connection.query` is _not_ an asynchronous function: it returns synchronously after initiating the query but before its completion. The callback is only called after the query has actually completed. See updated post about placing the `res.send`outside the callback. – traktor Dec 22 '20 at 00:54