3

I have a function that connects to a sql database, queries it, formats the results into an html table and returns the html variable:

function getData() {
    return new Promise((resolve, reject) => {
        var sql = require("mssql");
        var dbConfig = {
            server: "server",
            database: "db",
            user: "user",
            password: "pw"
        }
        var conn = new sql.Connection(dbConfig);
        var req = new sql.Request(conn);
        conn.connect(function (err) {
            if (err) {
                console.log(err);
                reject(err);
                return;
            }
            req.query("SELECT * FROM table",
                (err, recordset) => {
                    // Here we call the resolve/reject for the promise
                    try {
                        // If the results callback throws exception, it will be caught in 
                        // the catch block
                        resolve(resultsCallback(err, recordset));
                    }
                    catch (e) {
                        reject(e);
                    }
                }
            );

            conn.close();
        });
    })
}

function resultsCallback(err, recordset) {
    var tableify = require('tableify');
    if (err) {
        console.log(err);
        throw err;
    }
    else {
        var html = tableify(recordset);
        html = html.replace('<table>', '');
        html = html.replace('</table>', '');
        return html;
    }
};

And I am calling it like this:

getData().then((data)=>{console.log("Table data:",data);})
         .catch((error)=>{console.log("ERROR LOADING SQL:",error);})

However, for some reason the output from this is: Table Data: undefined

I am unsure why this would be happening like this. Did I return the data correctly?

Sal
  • 625
  • 2
  • 7
  • 11
  • 1
    Did you check that `recordset` is not `undefined`? – Duc Filan May 01 '18 at 00:18
  • No it is not `undefined`. I can write the contents of recordset to console.log – Sal May 01 '18 at 00:22
  • 1
    It looks like `return html` is the line that *should* run right before `resolve`ing, and that whatever `html` is should then be the `data` argument. But if you're able to `html = html.replace('', '');` without throwing an error, then `html` should be defined. Did you try logging around those lines to see if they're running and being populated as expected? – CertainPerformance May 01 '18 at 00:41
  • 1
    Try closing the connection when `req.query()` has completed. – Roamer-1888 May 01 '18 at 00:47
  • I've dummied up all the `external` functions (`conn.*` `req.*` `tableify`) and the code you've posted does not exhibit the issue - what is the content of `html` just after `var html = tableify(recordset);` – Jaromanda X May 01 '18 at 00:48

1 Answers1

-1

i think your resultsCallback is unnecessarily tangled up with error handling

i tried to clean up your example with some modern flair, hope it helps you out

const sql = require("mssql")
const tableify = require("tableify")

/**
* FORMAT RESULTS
*  - format sql records as html
*  - returns a string of html
*/
function formatResults(records) {
  return tableify(records)
    .replace("<table>", "")
    .replace("</table>", "")
}

/**
* GET DATA
*  - query records from a database
*  - returns a promised string of html
*/
async function getData({db, table}) {

  // open the sql connection pool
  const pool = await sql.connect(db)

  // query the database and format the results
  try {
    const results = await pool.request()
      .input("tablename", table)
      .query(`SELECT * from @tablename`)
    return formatResults(results)
  }

  // rethrow query errors
  catch (error) {
    error.message = `getData sql query error: ${error.message}`
    throw error
  }

  // always close the connection
  finally {
    pool.close()
  }
}

// USAGE EXAMPLE BELOW
;(async() => {

  const data = await getData({
    db: {
      server: "server",
      database: "db",
      user: "user",
      password: "pw"
    },
    table: "table"
  })

  console.log(data)

})().catch(error => console.error(error))
ChaseMoskal
  • 7,151
  • 5
  • 37
  • 50
  • There's no point making `getData` an async function - it's already returning a promise. Also, you can't use `await` on the top level like that. – CertainPerformance May 01 '18 at 01:36
  • it's good for the keyword `async` to remain everpresent regarding asynchronous functions — top level await fixed – ChaseMoskal May 01 '18 at 02:07
  • You can get rid of Promises (using [util.promisify](https://nodejs.org/api/util.html#util_util_promisify_original)) and callbacks altogether. Also you forgot to `connection.close()` which can be done pretty easily using await in try-catch-finally. [Checkout this gist](https://gist.github.com/laggingreflex/aa171d435cbf760d607b52861715464f) – laggingreflex May 01 '18 at 02:36
  • added `connection.close()` calls – ChaseMoskal May 01 '18 at 03:05
  • What problem does this actually fix? What was the problem with the OP's code? – jfriend00 May 01 '18 at 04:15
  • @jfriend00 — i'm not sure what the problem is, hopefully cleaning up the error handling like that will make it easier for OP or someone else to figure out — next time i might link a gist or something in a comment – ChaseMoskal May 01 '18 at 07:02
  • Composite promisifications generally turn out to be dogs' dinners. It would be far cleaner to promisify the two underlying asynchronisms separately then write a master routine to pull everything together. The [promise disposer pattern](https://stackoverflow.com/q/28915677/3478010) would make for an ultra clean solution. – Roamer-1888 May 02 '18 at 07:35
  • @Roamer-1888 — that's quite on target — i agree the only sane way to deal with this, is to set about promisifying the callback api's being used – ChaseMoskal May 02 '18 at 20:14
  • i've just now rewritten my answer's code to properly use the `mssql`'s modern async/await api — pay mind to the parameterized input (injection-proof) of the tablename via the `input` method – ChaseMoskal May 02 '18 at 20:44
  • OP — see the mssql package's readme instructions for more details https://github.com/tediousjs/node-mssql – ChaseMoskal May 02 '18 at 20:45
  • @laggingreflex — great suggestion about using `finally` for `.close()`! – ChaseMoskal May 02 '18 at 20:46