I think I found a more elegant solution to the problem.
Borrowing from the knex Transaction documentation, I will match their promise style with the async / await style that worked for me.
Promise style
var Promise = require('bluebird'); // Using trx as a transaction object: knex.transaction(function(trx) { var books = [ {title: 'Canterbury Tales'}, {title: 'Moby Dick'}, {title: 'Hamlet'} ]; knex.insert({name: 'Old Books'}, 'id') .into('catalogues') .transacting(trx) .then(function(ids) { return Promise.map(books, function(book) { book.catalogue_id = ids[0]; // Some validation could take place here. return knex.insert(book).into('books').transacting(trx); }); }) .then(trx.commit) .catch(trx.rollback); }) .then(function(inserts) { console.log(inserts.length + ' new books saved.'); }) .catch(function(error) { // If we get here, that means that neither the 'Old Books' catalogues insert, // nor any of the books inserts will have taken place. console.error(error); });
asynchronous / pending style
var Promise = require('bluebird'); // import Promise.map() // assuming knex.transaction() is being called within an async function const inserts = await knex.transaction(async function(trx) { var books = [ {title: 'Canterbury Tales'}, {title: 'Moby Dick'}, {title: 'Hamlet'} ]; const ids = await knex.insert({name: 'Old Books'}, 'id') .into('catalogues') .transacting(trx); const inserts = await Promise.map(books, function(book) { book.catalogue_id = ids[0]; // Some validation could take place here. return knex.insert(book).into('books').transacting(trx); }); }) await trx.commit(inserts); // whatever gets passed to trx.commit() is what the knex.transaction() promise resolves to. })
Documents state:
Issuing an error directly from a transaction handler function automatically rolls back the transaction, as does returning a rejected promise.
It appears that the transaction callback function is not expected to return anything or Promise. Declaring a callback as an asynchronous function means that it returns a Promise.
One of the advantages of this style is that you do not need to trigger a rollback manually. Returning a declined promise will automatically result in a rollback.
Be sure to pass all the results you want to use elsewhere for the final call to trx.commit ().
I checked this template in my own work and it works as expected.
source share