[Fixed] returning sqlite3 query in Node

Issue

I’m trying to write a function that returns a query from a sqlite3 database (using Node and Express)

This is how (likely) the function is called

app.get('/example',(req,res)=>{
    console.log(getThings(db_connection))
}

And this is the function per se

getThings(db){
    let sql = 'SELECT * FROM Table'
    let results[]
    db.all(sql, (err, rows) => {
        if(err){throw err}
        let i
        for(i=0;i<rows.length;i++){
            res.push(rows[i])
        }
        console.log(res)
    })
    return res
}

I expected the rows being returned at the end, but it always returns res before populating it first, and just then it prints res with the correctly

I might have understood why it does so, but I have no idea how to fix it properly (I’m still new at JS)

Solution

Callbacks are asynchronous, so res will not be populated before the return.

You need to make your callback into a Promise or use async/await.

Promisify the callback:

getThings(db){
   return new Promise((resolve, reject) => {
     db.all('SELECT * FROM Table', (err, rows) => {
       if (err) reject(err)

       resolve(rows)
     })
   })
}
app.get('/example', (req, res) => {
   getThings(db_connection)
    .then(rows => res.send(result))
    .catch(err => res.send({
       error: err.message
    }))
}

or

Use async/await:

Wrapped in try/catch to catch err, then because you’re simply looping over the rows you don’t need the for loop.

When you see the following structure object.method('param', (err, rows) => { you can almost guarantee its Promise compatible, else if not you can use util.promisify(original) to make it so.

Cleaner and more verbose then then().catch() chaining:

app.get('/example', async (req, res) => {
    let result = {}
    try {
       result = await getThings(db_connection)
    } catch (err) {
      result = {
       error: err.message
      }
    }
    res.send(result)
}
async getThings(db) {
    return db.all('SELECT * FROM Table')
}

Leave a Reply

(*) Required, Your email will not be published