Nicholas Kyriakides Nicholas Kyriakides - 1 year ago 505
Javascript Question

Get Knex.js transactions working with ES7 async/await

I'm trying to couple ES7's async/await with knex.js transactions.

Although I can easily play around with non-transactional code, I'm struggling to get transactions working properly using the aforementioned async/await structure.

I'm using this module to simulate async/await

Here's what I currently have:

Non-transactional version:



works fine but is not transactional

app.js



// assume `db` is a knex instance

app.post("/user", async((req, res) => {
const data = {
idUser: 1,
name: "FooBar"
}

try {
const result = await(user.insert(db, data));
res.json(result);
} catch (err) {
res.status(500).json(err);
}
}));


user.js



insert: async (function(db, data) {
// there's no need for this extra call but I'm including it
// to see example of deeper call stacks if this is answered

const idUser = await(this.insertData(db, data));
return {
idUser: idUser
}
}),

insertData: async(function(db, data) {
// if any of the following 2 fails I should be rolling back

const id = await(this.setId(db, idCustomer, data));
const idCustomer = await(this.setData(db, id, data));

return {
idCustomer: idCustomer
}
}),

// DB Functions (wrapped in Promises)

setId: function(db, data) {
return new Promise(function (resolve, reject) {
db.insert(data)
.into("ids")
.then((result) => resolve(result)
.catch((err) => reject(err));
});
},

setData: function(db, id, data) {
data.id = id;

return new Promise(function (resolve, reject) {
db.insert(data)
.into("customers")
.then((result) => resolve(result)
.catch((err) => reject(err));
});
}


Attempt to make it transactional



user.js



// Start transaction from this call

insert: async (function(db, data) {
const trx = await(knex.transaction());
const idCustomer = await(user.insertData(trx, data));

return {
idCustomer: idCustomer
}
}),


it seems that
await(knex.transaction())
returns this error:

[TypeError: container is not a function]

Answer Source

Async/await is based around promises, so it looks like you'd just need to wrap all the knex methods to return "promise compatible" objects.

Here is a description on how you can convert arbitrary functions to work with promises, so they can work with async/await:

Trying to understand how promisification works with BlueBird

Essentially you want to do this:

var transaction = knex.transaction;
knex.transaction = function(callback){ return knex.transaction(callback); }

This is because "async/await requires the either a function with a single callback argument, or a promise", whereas knex.transaction looks like this:

function transaction(container, config) {
  return client.transaction(container, config);
}

Alternatively, you can create a new async function and use it like this:

async function transaction() {
  return new Promise(function(resolve, reject){
    knex.transaction(function(error, result){
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
}

// Start transaction from this call

insert: async (function(db, data) {
 const trx = await(transaction());
 const idCustomer =  await(person.insertData(trx, authUser, data));

 return {
    idCustomer: idCustomer
  }
})

This may be useful too: Knex Transaction with Promises

(Also note, I'm not familiar with knex's API, so not sure what the params are passed to knex.transaction, the above ones are just for example).

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download