fawn

Fawn

Promise based Library for transactions in MongoDB

Fawn provides the ability to carry out edits on a mongoDB database as a series of steps. If an error occurs on any of the steps, the database is returned to its initial state (its state before the transaction started). It's based on the two phase commit system described in the MongoDB docs. Check out this Medium article for a more detailed look.

Getting Started:

Usage:

var Fawn =require("fawn");

Fawn.init("mongodb://127.0.0.1:27017/testDB");

or

var mongoose =require("mongoose");

mongoose.connect("mongodb://127.0.0.1:27017/testDB");

Fawn.init(mongoose);

Examples

Say you have two bank accounts, one belongs to John Smith and the other belongs to Broke Ass. You would like to transfer $20 from John Smith to Broke Ass. Assuming all first name and last name pairs are unique, this might look like:

Note: if you're running multiple apps connected to the same db, provide a string value for _collection that's unique to each app. Do this to avoid a situation where one app rolls back the unfinished transaction(s) of another app.

If you're using mongoose in your project initialize Fawn with mongoose:

task.initModel(modelName, schema): To initialize a model with a Schema.

Note: For model validation to work, run task with useMongoose set to true

Initalizes a mongoose model with the provided schema. If you're using mongoose, define your models with mongoose wherever possible. If the model has been defined by mongoose before this function is called, mongoose will throw an OverwriteModelError and if it was defined by Fawn, Fawn will throw an Error. Models can be defined only once.

The viaSave option allows you update a mongoose document using the save function. It's useful if you want to trigger mongoose pre save hooks. For this option to work you must run the task using mongoose

with mongoose:

var doc = someMongooseDocument;

doc.someProperty= newValue;

doc.save().then(console.log);

with Fawn:

var doc = someMongooseDocument;

var newDoc =doc.toObject();

newDoc.someProperty= newValue

task.update(doc, newDoc)

.options({viaSave:true})

.run({useMongoose:true})

.then(console.log);

Note: No changes will be made to to your database until you call task.run()

task.remove(model, condition): Remove document(s) from a collection

model (required): Name of the collection we're deleting from or a mongoose model or a mongoose document

Removes a file that matches "options" from the database using GridFS. The result of this operation is a GridStore instance (can be ignored). See GridStore

task.removeFile({_id: fileId})

.update("SomeCollection", updateConditions, updateData)

.run()

.then(function(results){

// if you need the gridStore instance

var gridStore = results[0];

})

.catch(function(err){

// Everything has been rolled back.

//log the error which caused the failure

console.log(err);

});

Note: No changes will be made to to your database until you call task.run()

task.run(options): Run a task.

options: {useMongoose: Boolean}

returns: Promise

For the database changes to occur, you must call task.run(). This function returns a promise. On success, the promise is resolved with an array containing the node-mongodb-native or mongoose result of each operation in sequence. If an error occurs, the promise is rejected with the error that caused the failure.

Fawn.Roller(): Get the Roller object.

returns: The Roller object

After initializing Fawn, get the Roller like so:

var Roller =Fawn.Roller();

Roller.roll(): Roll back all incomplete transcations

In case of a server crash or any other fatal error, use the roller to return all the documents affected by incomplete transactions to their original state. Should only be used when no tasks are in progress, usually on server startup.

var roller =Fawn.Roller();

roller.roll()

.then(function(){

// start server

});

Miscellaneous

Using the result of previous steps in subsequent steps

You might want to use the result of a previous step in a subsequent step. You can do this using a template object with the key "$ojFuture". Syntax: {$ojFuture: "indexOfStep.resultProperty1.property2.-----.propertyN"}. Here's how: