Node.js – Implementing Declarative Transactions

javanode.js

Back in the day, it was common to manage database transactions in Java by writing code that did it. Something like this:

Transaction tx = session.startTransaction();
...
try {
   tx.commit();
} catch (SomeException e){
   tx.rollback();
}

at the beginning and end of every method. This had some obvious problems – it's redundant, hides the intent of what's happening, etc. So, along came annotation-driven transactions:

@Transaction
public SomeResultObj getResult(...){

    ...
}

Is there any support for declarative transaction management in node.js?

Best Answer

In case you're using Express + Sequelize, you can do this by using a module called continuation-local-storage (henceforth cls, for brevity).

Show me the code

// When initializing your Sequelize instance
var Sequelize = require( "sequelize" );
Sequelize.cls = require( "continuation-local-storage" ).createNamespace( "db" );

module.exports = new Sequelize(...);

Then, create some middleware to initialize your transaction in every request (in case no one has come up with one yet):

var sequelize = require(...);
app.use(function ( req, res, next ) {
  sequelize.transaction(function () {
    // Do something else, but be sure to invoke next middleware
    next();    
  });
});

What's going on here?

  1. A cls namespace is declared for Sequelize to use. Must be set on the constructor;
  2. A transaction is initialized in an Express middleware;
  3. Every subsequent sequelize query will use the request transaction by default (you can override this behavior, obviously).

How this works?

This works because cls assigns a new storage for each new async chain (the request in this example), and they are passed down consistently and transparently.
It's some kind of magic that happens inside Node :)

Tips

It's very likely that you'll need to use some monkeypatch to take that approach. Depends on your Promises implementation.

Further info

Check the Sequelize documentation.

Related Topic