Get Knex.js transactions working with ES7 async/await

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).


Here is a way to write transactions in async / await.

It is working fine for MySQL.

const trx = await db.transaction();
try {
    const catIds = await trx('catalogues').insert({name: 'Old Books'});
    const bookIds = await trx('books').insert({catId: catIds[0], title: 'Canterbury Tales' });
    await trx.commit();
} catch (error) {
    await trx.rollback(error);
}

I couldn't find a solid answer for this anywhere (with rollbacks and commits) so here's my solution.

First you need to "Promisify" the knex.transaction function. There are libraries for this, but for a quick example I did this:

const promisify = (fn) => new Promise((resolve, reject) => fn(resolve));

This example creates a blog post and a comment, and rolls back both if there's an error with either.

const trx = await promisify(db.transaction);

try {
  const postId = await trx('blog_posts')
  .insert({ title, body })
  .returning('id'); // returns an array of ids

  const commentId = await trx('comments')
  .insert({ post_id: postId[0], message })
  .returning('id'); 

  await trx.commit();
} catch (e) {
  await trx.rollback();
}

For those who come in 2019.

After I updated Knex to version 0.16.5. sf77's answer doesn't work anymore due to the change in Knex's transaction function:

transaction(container, config) {
  const trx = this.client.transaction(container, config);
  trx.userParams = this.userParams;
  return trx;
}

Solution

Keep sf77's promisify function:

const promisify = (fn) => new Promise((resolve, reject) => fn(resolve));

Update trx

from

const trx = await promisify(db.transaction);

to

const trx =  await promisify(db.transaction.bind(db));