Fawn
Promise based Library for atomic-ish operations 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 it's initial state (it's state before the transaction started).
View on GitHub If you're having trouble reading this on npm
Getting Started:
Install node.js and mongoDB
Start mongoDB in a terminal: mongod
Then:
npm install fawn
Usage:
var Fawn = require("fawn");
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:
var task = Fawn.Task()
task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}})
.update("Accounts", {firstName: "Broke", lastName: "Ass"}, {$inc: {balance: 20}})
.run()
.then(function(){
})
.catch(function(err){
console.log(err);
});
if you prefer not to chain function calls, you don't have to:
task.update("Accounts", {firstName: "Broke", lastName: "Ass"}, {$inc: {balance: -20}})
task.update("Accounts", {firstName: "Coke", lastName: "Dealer"}, {$inc: {balance: 20}})
task.run()
.then(function(){
})
.catch(function(err){
console.log(err);
});
The server could crash before a task is complete, You can use the Roller to rollback all incomplete transactions before starting your server.
var roller = Fawn.Roller();
roller.roll()
.then(function(){
});
API
Fawn.init(db, _collection, options): Initialize Fawn
db (required): mongoose instance or connection string
_collection (optional): Name of collection used internally by Fawn to store transactions
options (optional. lol): Connection options. Same as mongoose connection options
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:
var mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/testDB");
Fawn.init(mongoose, "Fawn_collection_name_if_you_want_to_specify");
Without mongoose, Initialze Fawn like so:
var options = {
user: "teh_huose_kat",
pass: "teh_Kitti_passwrod"
}
var collection = "Fawn_collection_name_if_you_want_to_specify";
Fawn.init("mongodb://127.0.0.1:27017/testDB", collection || null, options || null);
###
Fawn.Task(): Create a Fawn task
returns: A new task
After intitializing Fawn, create a task like so:
var task = Fawn.Task();
###
task.initModel(modelName, schema): To initialize a model with a Schema.
modelName (required): Name of the collection associated with this model
schema (required): Same as object passed to mongoose Schema. Also see validation
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.
var schema = {
name: {type: String, required: true}
, specials: [{title: String, year: Number}]
}
task.initModel("comedians", schema);
Save operations to the "comedians" model will validate against the schema;
###
task.save(model, doc): To save a document
model (required): Name of the collection we're saving to or a mongoose model or a mongoose document
doc (optional): Object to save or a mongoose document
these are all valid:
var Cars = mongoose.model("cars", new Schema({make: String, year: Number}));
var toyota = new Cars({make: "Toyota", year: 2015});
task.save("cars", {make: "Toyota", year: 2015});
task.save(Cars, {make: "Toyota", year: 2015});
task.save("cars", toyota);
task.save(Cars, toyota);
task.save(toyota);
Note: No changes will be made to to your database until you call task.run()
task.update(model, condition, data): To update a document
model (required): Name of the collection we're updating or a mongoose model or a mongoose document
condition (required): Same as in mongoose and mongodb
data (optional): Data to update with same as in mongoose and mongodb
These are all valid
var Cars = mongoose.model("cars", new Schema({make: String, year: Number}));
task.update("cars", {make: "Toyota"}, {year: 2016});
task.update(Cars, {make: "Toyota"}, {year: 2016});
Cars.findOne({make: "Toyota"}, function(toyota){
task.update(toyota, {year: 2016});
});
Note: No changes will be made to to your database until you call task.run()
###
task.options(options): Add options to an update task.
options (required): Update options - same as in mongoose
Attach to update call as shown
task.update("cars", {make: "Toyota"}, {year: 2016})
.options({multi: true});
task.update("cars", {make: "Ford"}, {year: 2016});
task.options({multi: true});
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
condition (optional): Same as in mongoose
These are all valid
var Cars = mongoose.model("cars", new Schema({make: String, year: Number}));
task.remove("cars", {year: 2015});
task.remove(Cars, {year: 2015});
Cars.findOne({year: 2015}, function(car){
task.remove(car);
})
Note: No changes will be made to to your database until you call task.run()
###
task.run(): Run a task.
returns: Promise
For the database changes to occur, you must call task.run(). This function returns a promise. If an error occurs, the promise is rejected with the error that caused the failure.
task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}})
.update("Accounts", {firstName: "Broke", lastName: "Ass"}, {$inc: {balance: 20}})
.run()
.then(function(){
})
.catch(function(err){
console.log(err);
});
###
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
Returns 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(){
});
Test
To test this module, start mongodb in a terminal
mongod
Then cd to the project directory and run
npm test