EggBornJS provides more convenient database transaction support. Let’s look at how database transaction is supported in EggJS and EggBornJS respectively

EggJS

const conn = await app.mysql.beginTransaction(); // begin

try {
  await conn.insert(table, row1);  // step one
  await conn.update(table, row2);  // step two
  await conn.commit(); // commit
} catch (err) {
  // error, rollback
  await conn.rollback(); // must rollback on error!!
  throw err;
}

EggBornJS

In EggBornJS, there is no need to change the code related to accessing the database, just declare the middleware transaction in the API route

src/module/test-party/backend/src/routes.js

// test
{ method: 'post', path: 'kitchen-sink/guide/echo8', controller: testKitchensinkGuide, middlewares: 'transaction' },

Whether ctx.db or model is used to operate the database, when the middleware transaction is enabled, the same database connection object is automatically maintained in the context environment, thus facilitating support for database transaction

API Route Performing-Chain

As mentioned earlier, One API Route can be performed by another API Route in backend, thus forming the performing-chain

Since each API Route can specify middleware transaction separately, then what are the operaion rules of transaction in the performing-chain?

Basic Rules

  1. At most one database transaction can be created in an performing-chain. that is, there is no concept of child transaction or nested transaction
  2. When the first API Route has enabled transaction, the subsequent API Routes will automatically be in the previously enabled transaction regardless of whether specified the middleware transaction

Perform after Transaction

In some special scenarios, codes need to be performed after the current database transaction is committed. At this time, you can register an asynchronous method through the method of ctx.tail

src/module/test-party/backend/src/controller/test/ctx/tail.js

const require3 = require('require3');
const assert = require3('assert');

module.exports = app => {

  class TailController extends app.Controller {

    async tail() {
      // 1
      this.ctx.meta._cache = 1;

      // tail
      this.ctx.tail(() => {
        assert.equal(this.ctx.meta._cache, 2);
      });

      // 2
      this.ctx.meta._cache = 2;

      // done
      this.ctx.success();
    }

  }

  return TailController;
};