david
david

Reputation: 421

Knex Transaction with Promises

I am getting the correct output, and indeed, these two operations are being treated as a single transactional unit; where if one fails, both fail.

In this code example: i am doing a transaction of

(1) insert (2) update

The way I approach it is to nest my db operations inside the .then. My question is if this code is correct by accident? i am new to promises and knex.

knex.transaction(function(t) {
   knex('foo')
   .transacting(t)
   .insert({id:"asdfk", username:"barry", email:"[email protected]"})
   .then(function() {
       knex('foo')
       .where('username','=','bob')
       .update({email:"[email protected]"})
       .then(t.commit, t.rollback)
   })
})
.then(function() {
 // it worked
},
function() {
 // it failed
});

This works, but I feel like I am doing something wrong still. Looking for comments.

Upvotes: 20

Views: 49363

Answers (2)

Gunjan
Gunjan

Reputation: 2805

I was trying out the accepted answer here. It was throwing me some errors like "Transaction query is already complete" and "Database locked". The answer is old, so might be working with previous version. I am using Sqlite3.34.1 and knex0.95.4. The code worked for me with some tweaks. Adding in this thread, It could help someone.

async function process() {
    await knex.transaction(function(t) {
        return knex('foo')
        .transacting(t)
        .insert({id:"asdfkg", username:"bob", email:"[email protected]"})
        .then(function() {
            return t('foo').insert({id:"abcd", username:"john", email:"[email protected]"})
        })
        .then(function() {
            return t('foo')
            .where('username','=','bob')
            .update({email:"[email protected]"});
        })
    })
    .then(function() {
    console.log("it worked")
    })
    .catch(function(e) {
    console.log(e)
    console.log("It failed")
    });
    knex.destroy()
}

I think, rollback and commit is taken care by its own, we wont have to specify it explicitly.

Upvotes: 5

Esailija
Esailija

Reputation: 140210

You need to return a promise from the inner query in order for the outer chain to be chained with that.

You also swallow any errors because you don't rethrow them - it's better to use .catch() for this reason because it makes it more clearer what is happening - that is what would happen with normal try-catch statement.

knex.transaction(function(t) {
   return knex('foo')
   .transacting(t)
   .insert({id:"asdfk", username:"barry", email:"[email protected]"})
   .then(function() {
        return knex('foo')
           .where('username','=','bob')
           .update({email:"[email protected]"});
   })
   .then(t.commit)
   .catch(function(e) {
        t.rollback();
        throw e;
   })
})
.then(function() {
 // it worked
})
.catch(function(e) {
 // it failed
});

To understand it better, here's the synchronous version that is being "emulated":

try {
    var t = knex.transaction();
    try {
        knex("foo")
            .transacting(t)
            .insert({id:"asdfk", username:"barry", email:"[email protected]"});
        knex("foo")
            .where('username','=','bob')
            .update({email:"[email protected]"});
        t.commit();
    }
    catch (e) {
        t.rollback();
        // As you can see, if you don't rethrow here
        // the outer catch is never triggered
        throw e;
    }
    // It worked
}
catch (e) {
    //It failed
}

Upvotes: 39

Related Questions