{"id":15040547,"url":"https://github.com/e-oj/fawn","last_synced_at":"2025-05-16T10:05:45.901Z","repository":{"id":9200390,"uuid":"61229738","full_name":"e-oj/Fawn","owner":"e-oj","description":"Transactions for MongoDB (See the README)","archived":false,"fork":false,"pushed_at":"2023-04-17T07:14:34.000Z","size":266,"stargazers_count":484,"open_issues_count":33,"forks_count":54,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-05-16T10:04:24.854Z","etag":null,"topics":["mongodb","mongoose","nodejs","transaction"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/fawn","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/e-oj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-06-15T18:10:42.000Z","updated_at":"2025-05-04T17:55:24.000Z","dependencies_parsed_at":"2024-01-13T10:42:05.809Z","dependency_job_id":"0951920f-0096-4603-8d7f-04662d1482fb","html_url":"https://github.com/e-oj/Fawn","commit_stats":{"total_commits":301,"total_committers":8,"mean_commits":37.625,"dds":0.3588039867109635,"last_synced_commit":"88cd8e0636dff88596ee30edb45e1705ecb7d931"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e-oj%2FFawn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e-oj%2FFawn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e-oj%2FFawn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/e-oj%2FFawn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/e-oj","download_url":"https://codeload.github.com/e-oj/Fawn/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254509477,"owners_count":22082891,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mongodb","mongoose","nodejs","transaction"],"created_at":"2024-09-24T20:44:43.538Z","updated_at":"2025-05-16T10:05:45.884Z","avatar_url":"https://github.com/e-oj.png","language":"JavaScript","readme":"## [![Travis](https://img.shields.io/travis/e-oj/Fawn.svg?style=flat-square)](https://travis-ci.org/e-oj/Fawn) [![npm](https://img.shields.io/npm/l/fawn.svg?style=flat-square)](https://www.npmjs.com/package/fawn) [![npm](https://img.shields.io/npm/v/fawn.svg?style=flat-square)](https://www.npmjs.com/package/fawn) [![npm](https://img.shields.io/npm/dt/fawn.svg?style=flat-square)](https://www.npmjs.com/package/fawn)\n\n### Quick Note:\n\u003e I no longer have the time to actively maintain this project. Transactions have been added to MongoDB but I've been informed that some users, for varying reasons, are stuck with an older version of the database and still depend on this library. I'll need volunteers to tackle the open issues. If you're interested, please open an issue indicating your interest and I'll be happy to add you to the project. Thanks in advance.\n\n# Fawn\n## Promise based Library for transactions in MongoDB\n\nFawn 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](https://codeburst.io/fawn-transactions-in-mongodb-988d8646e564) for a more detailed look.\n\n**View on \u003ca href=\"https://github.com/e-oj/Fawn\" target=\"_blank\"\u003eGitHub\u003c/a\u003e**\n\n- [Getting Started](#getting_started)\n- [Usage](#usage)\n- [API](#api)\n- [Misc.](#misc)\n- [Test](#test)\n\n## \u003ca name=\"getting_started\"\u003e\u003c/a\u003eGetting Started:\n\nInstall [node.js](https://nodejs.org) and [mongoDB](https://www.mongodb.com/download-center)\n\nStart mongoDB in a terminal: ```mongod```\n\nThen:\n```npm install fawn```\n\n## \u003ca name=\"usage\"\u003e\u003c/a\u003eUsage:\n\n```javascript\nvar Fawn = require(\"fawn\");\n\nFawn.init(\"mongodb://127.0.0.1:27017/testDB\");\n```\nor\n```javascript\nvar mongoose = require(\"mongoose\");\nmongoose.connect(\"mongodb://127.0.0.1:27017/testDB\");\n\nFawn.init(mongoose);\n```\n\n### \u003ca name=\"examples\"\u003e\u003c/a\u003eExamples\nSay 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:\n\n```javascript\nvar task = Fawn.Task();\n\n//assuming \"Accounts\" is the Accounts collection\ntask.update(\"Accounts\", {firstName: \"John\", lastName: \"Smith\"}, {$inc: {balance: -20}})\n  .update(\"Accounts\", {firstName: \"Broke\", lastName: \"Ass\"}, {$inc: {balance: 20}})\n  .run()\n  .then(function(results){\n    // task is complete \n\n    // result from first operation\n    var firstUpdateResult = results[0];\n\n    // result from second operation\n    var secondUpdateResult = results[1];\n  })\n  .catch(function(err){\n    // Everything has been rolled back.\n    \n    // log the error which caused the failure\n    console.log(err);\n  });\n```\n[GridFS]: \u003chttps://docs.mongodb.com/manual/core/gridfs/\u003e\n\nFiles can be saved to and removed from [GridFS][]. Here's how you might update a user's profile image:\n```javascript\nvar newImageId = someMongoDbId;\n\ntask.saveFile(\"/path/to/new/profile/img\", {_id: newImageId, filename: \"profile.png\"})\n  .removeFile({_id: oldImageId})\n  .update(\"users\", {_id: userId}, {profileImageId: newImageId})\n  .run()\n  .then(function(results){\n    var newImgFile = results[0];\n    \n    console.log(newImgFile.filename) // profile.png\n  })\n  .catch(function(err){\n    // Everything has been rolled back.\n    \n    // log the error which caused the failure\n    console.log(err);\n  });\n```\n\nBy default, tasks run using the native driver but you can opt for mongoose. If you prefer not to chain function calls, you don't have to:\n\n```javascript\ntask.update(\"Accounts\", {firstName: \"Broke\", lastName: \"Ass\"}, {$inc: {balance: -20}})\ntask.update(\"Accounts\", {firstName: \"The\", lastName: \"Plug\"}, {$inc: {balance: 20}})\ntask.run({useMongoose: true})\n  .then(function(){\n    // update is complete\n  })\n  .catch(function(err){\n    // Everything has been rolled back.\n    \n    // log the error which caused the failure\n    console.log(err);\n  });\n```\n\nThe server could crash before a task is complete, You can use the Roller to rollback all incomplete transactions before starting your server:\n\n```javascript\n// assuming Fawn has been initialized. See Fawn.init below\nvar roller = Fawn.Roller();\n\nroller.roll()\n  .then(function(){\n    // start server\n  });\n```\n\n## \u003ca name=\"api\"\u003e\u003c/a\u003eAPI\n\n- [Fawn.init](#fawn_init)\n- [Fawn.Task](#fawn_task)\n- [task.initmodel](#task_initmodel)\n- [task.save](#task_save)\n- [task.update](#task_update)\n- [task.options](#task_options)\n- [task.remove](#task_remove)\n- [task.saveFile](#task_savefile)\n- [task.removeFile](#task_removefile)\n- [task.run](#task_run)\n- [Fawn.Roller](#fawn_roller)\n- [Roller.roll](#roller_roll)\n\n### \u003ca name=\"fawn_init\"\u003e\u003c/a\u003eFawn.init(db, _collection, options): Initialize Fawn\n\n\u003e db (required): [mongoose](https://github.com/Automattic/mongoose) instance or [connection string](https://docs.mongodb.com/manual/reference/connection-string/)\n\n\u003e _collection (optional): Name of collection used internally by Fawn to store transactions\n\n\u003e options (optional. lol): Connection options. Same as [mongoose connection options](http://mongoosejs.com/docs/connections.html#options)\n\n\u003cbr\u003e **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.**\n\nIf you're using mongoose in your project initialize Fawn with mongoose:\n\n```javascript\nvar mongoose = require(\"mongoose\");\n\nmongoose.connect(\"mongodb://127.0.0.1:27017/testDB\");\n\n// remember, _collection is optional\nFawn.init(mongoose, \"Fawn_collection_name_if_you_want_to_specify\");\n```\n\nWithout mongoose, Initialize Fawn like so:\n\n```javascript\n// options object (http://mongoosejs.com/docs/connections.html#options)\nvar options = {\n  user: \"teh_huose_kat\",\n  pass: \"teh_Kitti_passwrod\"\n};\n\nvar collection = \"Fawn_collection_name_if_you_want_to_specify\";\n\n// remember, _collection and options are optional\nFawn.init(\"mongodb://127.0.0.1:27017/testDB\", collection || null, options || null);\n```\n\u003cbr\u003e\n\n### \u003ca name=\"fawn_task\"\u003e\u003c/a\u003eFawn.Task(): Create a Fawn task\n  \n  \u003e returns: A new task\n\nAfter intitializing Fawn, create a task like so:\n\n```javascript\nvar task = Fawn.Task();\n```\n\u003cbr\u003e\n\n### \u003ca name=\"task_initmodel\"\u003e\u003c/a\u003etask.initModel(modelName, schema): To initialize a model with a Schema.\n\n  \u003e modelName (required): Name of the collection associated with this model\n  \n  \u003e schema (optional): Same as object passed to [mongoose Schema](http://mongoosejs.com/docs/guide.html#definition). Also see [validation](http://mongoosejs.com/docs/validation.html)\n  \n  *Note: For model validation to work, run task with useMongoose set to true*\n  \u003cbr\u003e\n\u003cbr\u003eInitalizes 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.\n  \n  ```javascript\n  var schema = {\n    name: {type: String, required: true}\n    , specials: [{title: String, year: Number}]\n  };\n  \n  task.initModel(\"comedians\", schema)\n    .save(\"comedians\", {name: \"Kevin Hart\", specials: [{title: \"What Now\", year: 2016}]})\n    .run({useMongoose: true})\n    .then(function(results){\n      console.log(results);\n    });\n  ```\n  \n  Save operations to the \"comedians\" model will validate against the schema;\n\n\u003cbr\u003e\n\n### \u003ca name=\"task_save\"\u003e\u003c/a\u003etask.save(model, doc): To save a document\u003c/b\u003e\n\n  \u003e model (required): Name of the collection we're saving to or a mongoose model or a mongoose document\n\n  \u003e doc (optional): Object to save or a mongoose document\n\t\n  \u003cbr\u003ethese are all valid:\n  \n  ```javascript\n  var Cars = mongoose.model(\"cars\", new Schema({make: String, year: Number}));\n  var toyota = new Cars({make: \"Toyota\", year: 2015});\n\n  task.save(\"cars\", {make: \"Toyota\", year: 2015});\n  task.save(Cars, {make: \"Toyota\", year: 2015});\n  task.save(\"cars\", toyota);\n  task.save(Cars, toyota);\n  task.save(toyota);\n  ```\n  \n  *Note: No changes will be made to to your database until you call task.run()*\n  \n\u003cbr\u003e \n\n[mongoose]: \u003chttp://mongoosejs.com/docs/api.html#model_Model.update\u003e \n\n[mongodb]: \u003chttps://docs.mongodb.com/manual/core/document/#document-query-filter\u003e\n\n### \u003ca name=\"task_update\"\u003e\u003c/a\u003etask.update(model, condition, data): To update a document\n\n  \u003e model (required): Name of the collection we're updating or a mongoose model or a mongoose document\n\n  \u003e condition (required): Same as in [mongoose][] and [mongodb][]\n  \n  \u003e data (optional): Data to update with same as in [mongoose][] and [mongodb](https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-parameter)\n  \n  \u003cbr\u003e These are all valid\n \n  ```javascript\n  var Cars = mongoose.model(\"cars\", new Schema({make: String, year: Number}));\n \n  task.update(\"cars\", {make: \"Toyota\"}, {year: 2016});\n  task.update(Cars, {make: \"Toyota\"}, {year: 2016});\n  \n  Cars.findOne({make: \"Toyota\"}, function(toyota){\n    task.update(toyota, {year: 2016});\n  });\n ```\n \n  *Note: No changes will be made to to your database until you call task.run()*\n  \n  \u003cbr\u003e\n  \n### \u003ca name=\"task_options\"\u003e\u003c/a\u003etask.options(options): Add options to an update task.\n\n  \u003e options (required): Update options = [mongoose][] options + {viaSave: Boolean}\n  \n  \u003cbr\u003e Attach to update call as shown\n  \n  ```javascript\n  task.update(\"cars\", {make: \"Toyota\"}, {year: 2016})\n    .options({multi: true});\n  \n  // Also valid\n  \n  task.update(\"cars\", {make: \"Ford\"}, {year: 2016});\n  task.options({multi: true});\n  ```\n  \n  The \u003cb\u003eviaSave\u003c/b\u003e option allows you update a \u003cb\u003e\u003ci\u003emongoose\u003c/i\u003e\u003c/b\u003e document using the save function. It's useful if you want to trigger mongoose pre save hooks. \u003cb\u003e\u003ci\u003eFor this option to work you must run the task using mongoose\u003c/i\u003e\u003c/b\u003e\n\nwith mongoose:\n```javascript\n  var doc = someMongooseDocument;\n  \n  doc.someProperty = newValue;\n  doc.save().then(console.log);\n```\n\nwith Fawn:\n```javascript\n  var doc = someMongooseDocument;\n  var newDoc = doc.toObject();\n  \n  newDoc.someProperty = newValue\n  \n  task.update(doc, newDoc)\n    .options({viaSave: true})\n    .run({useMongoose: true})\n    .then(console.log);\n  ```\n  *Note: No changes will be made to to your database until you call task.run()*\n\n  \u003cbr\u003e\n  \n### \u003ca name=\"task_remove\"\u003e\u003c/a\u003etask.remove(model, condition): Remove document(s) from a collection\n\n  \u003e model (required): Name of the collection we're deleting from or a mongoose model or a mongoose document\n  \n  \u003e condition (optional): Same as in [mongoose](http://mongoosejs.com/docs/api.html#query_Query-remove)\n  \n  \u003cbr\u003e These are all valid\n  \n  ```javascript\n  var Cars = mongoose.model(\"cars\", new Schema({make: String, year: Number}));\n  \n  // removes all cars with year === 2015\n  task.remove(\"cars\", {year: 2015});\n  task.remove(Cars, {year: 2015});\n  \n  Cars.findOne({year: 2015}, function(car){\n    // remove just this car\n    task.remove(car);\n  });\n  ```\n\n  *Note: No changes will be made to to your database until you call task.run()*\n\n  \u003cbr\u003e \n  \n### \u003ca name=\"task_savefile\"\u003e\u003c/a\u003etask.saveFile(filePath, options): Save a file to the db via [GridFS][]\n\n  \u003e filePath (required): Path to the file \n  \n  \u003e options (optional): Same as [GridStore options][]\n  \n  [GridStore options]: \u003chttp://mongodb.github.io/node-mongodb-native/api-generated/gridstore.html#constructor\u003e\n  \n  Saves the file at \"filePath\" to the database using GridFS. The result of this operation is the saved file's object. See [File object](https://docs.mongodb.com/manual/core/gridfs/#the-files-collection)\n  \n  ```javascript\n  task.saveFile(\"path/to/some/file\", {filename: \"a_string_filename.ext\"})\n    .update(\"SomeCollection\", updateConditions, updateData)\n    .run()\n    .then(function(results){\n      var file = results[0];\n      \n      console.log(file.filename); // a_string_filename.ext\n    }).catch(function(err){\n      // Everything has been rolled back.\n      \n      //log the error which caused the failure\n      console.log(err);\n    });\n  ```\n\n  *Note: No changes will be made to to your database until you call task.run()*\n\n  \u003cbr\u003e \n\n### \u003ca name=\"task_removefile\"\u003e\u003c/a\u003etask.removeFile(options): Remove a file from the db via [GridFS][]\n\n  \u003e options (required): Same as [GridStore options][]\n  \n  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]\n  \n  [GridStore]: \u003chttp://mongodb.github.io/node-mongodb-native/api-generated/gridstore.html\u003e\n  \n  ```javascript\n  task.removeFile({_id: fileId})\n    .update(\"SomeCollection\", updateConditions, updateData)\n    .run()\n    .then(function(results){\n      // if you need the gridStore instance\n      var gridStore = results[0];\n    })\n    .catch(function(err){\n      // Everything has been rolled back.\n      \n      //log the error which caused the failure\n      console.log(err);\n    });\n  ```\n\n  *Note: No changes will be made to to your database until you call task.run()*\n\n  \u003cbr\u003e \n  \n### \u003ca name=\"task_run\"\u003e\u003c/a\u003etask.run(options): Run a task.\n\n  \u003e options: {useMongoose: Boolean}\n  \n  \u003e returns: Promise\n\n  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](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html) or [mongoose](http://mongoosejs.com/docs/api.html) result of each operation in sequence. If an error occurs, the promise is rejected with the error that caused the failure.\n  \n  ```javascript\n  task.update(\"Accounts\", {firstName: \"John\", lastName: \"Smith\"}, {$inc: {balance: -20}})\n    .update(\"Accounts\", {firstName: \"Broke\", lastName: \"Ass\"}, {$inc: {balance: 20}})\n    .run() // or run({useMongoose: true}); \n    .then(function(results){\n      //task is complete \n\n      //result from first operation\n      var firstUpdateResult = results[0];\n\n      //result from second operation\n      var secondUpdateResult = results[1];\n    })\n    .catch(function(err){\n      // Everything has been rolled back.\n      \n      //log the error which caused the failure\n      console.log(err);\n    });\n  ```\n  \u003ca name=\"task_run_results\"\u003e Results Reference:\n  - the result of save is, [insertOneWriteOpResult](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~insertOneWriteOpResult) for mongodb native, and the saved doc for mongoose\n  - the result of remove is, [deleteWriteOpResult](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~deleteWriteOpResult) for mongodb native, and [writeOpResult](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult) for mongoose\n  - the result of update is, [updateWriteOpResult](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~updateWriteOpResult) for mongodb native, and the [mongodb update output](https://docs.mongodb.com/v2.6/reference/command/update/#output) for mongoose\n  - the result of saveFile is the saved file object\n  - the result of removeFile is a [GridStore][] instance\n  \u003cbr\u003e\n  \n### \u003ca name=\"fawn_roller\"\u003e\u003c/a\u003eFawn.Roller(): Get the Roller object.\n  \n  \u003e returns: The Roller object\n  \n  After initializing  Fawn, get the Roller like so:\n  \n  ```javascript\n  var Roller = Fawn.Roller();\n  ```\n \u003cbr\u003e\n \n### \u003ca name=\"roller_roll\"\u003e\u003c/a\u003eRoller.roll(): Roll back all incomplete transcations\n  \n  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.\n  \n  ```javascript\n  var roller = Fawn.Roller();\n  \n  roller.roll()\n    .then(function(){\n      // start server\n    });\n  ```\n  \u003cbr\u003e\n  \n## \u003ca name=\"misc\"\u003e\u003c/a\u003eMiscellaneous \n\n### Using the result of previous steps in subsequent steps\n  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:\n  \n  ```javascript\n  task.save(\"Kids\", {name: {full: \"Brody Obi\"}}) //result will be {_id: someMongoId, name: {full: \"Brody Obi\"}}\n    .update(\"Parents\", {_id: parentId}, {firstChild: {id: {$ojFuture: \"0._id\"} , fullName: {$ojFuture: \"0.name.full\"}})\n    .run({useMongoose: true})\n    .then(function(){\n    \t// task is complete\n    })\n    .catch(function(err){\n      // Everything has been rolled back.\n    \n      //log the error which caused the failure\n      console.log(err);\n    });\n  ```\n  To use this feature you need to know the exact format of the step's result. For Reference: [Results](#task_run_results)\n  \n  \n## \u003ca name=\"test\"\u003e\u003c/a\u003eTest\n\n  To test this module, start mongodb in a terminal\n  \n  ```\n  mongod\n  ```\n  \n  Then cd to the project directory and run \n  \n  ```\n  npm test\n  ```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fe-oj%2Ffawn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fe-oj%2Ffawn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fe-oj%2Ffawn/lists"}