transactions-mongoose
Version:
Transactions for mongoose
193 lines (148 loc) β’ 6.77 kB
Markdown
# πΊπ¦ Transactions for mongoose
[](https://github.com/rosbitskyy/transactions-mongoose/actions)
[](https://www.npmjs.com/package/transactions-mongoose)
[](http://www.npmtrends.com/transactions-mongoose)
[](https://snyk.io/test/npm/transactions-mongoose)
**Transactions allow you to perform multiple group operations in isolation and undo all operations if one of them fails.**
This module allows you to combine different transactions and operations into a group and execute them together.
Pre-checks the schema for errors, duplication of unique indexes/fields
Very simple and extremely easy βοΈ :
- A couple of methods for everything
- Mix executors as you like
- Use the session for your usual, standard saving
And yes, let's start!
βοΈ [buy me a coffee](https://www.buymeacoffee.com/rosbitskyy.ruslan)
### Installation
First install [Node.js](https://nodejs.org/uk) and [MongoDB](https://www.mongodb.com/try). Then:
```shell
npm i transactions-mongoose
```
Mongoose [latest](https://www.npmjs.com/package/transactions-mongoose?activeTab=dependencies) also included
### Usage
```javascript
const {Transaction} = require("transactions-mongoose");
const transaction = new Transaction();
// or with debug log
const transaction = new Transaction().setSendbox(true);
```
## An exemplary use case
### [Create / Insert new document](https://github.com/rosbitskyy/transactions-mongoose/blob/main/tests/create-insert.js)
```javascript
const {Transaction} = require("transactions-mongoose");
const transaction = new Transaction().setSendbox(true);
const transactionData1 = transaction.add({
Person,
firstname: 'Sancho',
lastname: 'Panse',
age: 22,
sex: 'male',
status: 'free'
});
const transactionData2 = transaction.add({
Model: 'Person',
firstname: 'Janna',
lastname: 'Dark',
age: 21,
sex: 'female',
status: 'free'
});
transaction.add(Person, {
firstname: 'Hulio',
lastname: 'Iglessias',
age: 35,
sex: 'male',
status: 'free'
});
await transaction.commit();
console.log('transaction 1 result', transactionData1.result) // the result of the save() operation
console.log('transaction 2 document', transactionData2.document)
```
### [Update an existing one](https://github.com/rosbitskyy/transactions-mongoose/blob/main/tests/update-existing.js)
```javascript
const {Transaction} = require("transactions-mongoose");
const transaction = new Transaction().setSendbox(true);
// variant #1 - use standard setters
let personSancho = await Person.findById('...Sancho id');
personSancho.age += 1;
personSancho.status = 'married';
personSancho.friend_id = '...Hulio Iglessias id';
transaction.add(personSancho);
// variant #2 - by document and update object
let personJanna = await Person.findById('...Janna id');
transaction.add(personJanna).update({
age: ++personJanna.age,
status: 'married',
friend_id: personSancho._id,
bodyFriend_id: '...Hulio Iglessias id',
});
// variant #3 - by ObjectId
transaction.add(Person, {_id: personSancho._id}).update({
friend_id: personJanna._id
});
transaction.add({Person, _id: personSancho._id}).update({
friend_id: personJanna._id
});
await transaction.commit();
```
### [Executing an isolated block that may fail is not related to Mongo but affects whether the data is saved or not.](https://github.com/rosbitskyy/transactions-mongoose/blob/main/tests/execute.js)
```javascript
const fetch = require("node-fetch");
const {Transaction} = require("transactions-mongoose");
const transaction = new Transaction().setSendbox(true);
const getAvatar = async (id) => {
const response = await fetch('https://i.pravatar.cc/300?u=' + id);
const blob = await response.blob()
return "data:" + blob.type + ';base64,' + Buffer.from(await blob.arrayBuffer()).toString('base64');
};
const transactionData = transaction.execute(async () => {
let personSancho = await Person.findById('...Sancho id');
transaction.add(personSancho).update({
updatedAt: Date.now(),
__v: ++personSancho.__v
});
// there may be a timeout error or a reader processing error
personSancho.avatar = await getAvatar(personSancho._id)
transaction.execute(async () => {
let personHulio = await Person.findById('...Hulio id');
personHulio.avatar = await getAvatar(personHulio._id);
transaction.add(personHulio)
});
let personJanna = await Person.findById('...Janna id');
personJanna.avatar = await getAvatar(personJanna._id);
const td = transaction.add(personJanna)
// The result can be whatever you want
// we will return the Janna document update result
// https://mongoosejs.com/docs/api/query.html#Query.prototype.updateOne()
return td
});
// and also execute it :)
personJanna.updatedAt = Date.now()
transaction.add(personJanna)
await transaction.commit();
console.log('transaction result', transactionData.result.result);
```
### [With session executor](https://github.com/rosbitskyy/transactions-mongoose/blob/main/tests/sessions.js)
To use with Mongo Replica set
```javascript
const {Transaction} = require("transactions-mongoose");
const transaction = new Transaction().setSendbox(true);
transaction.session(async (session) => {
let personSancho = await Person.findById('...Sancho id').session(session);
let personJanna = await Person.findById('...Janna id').session(session);
let personHulio = await Person.findById('...Hulio id').session(session);
personSancho.age = 100; // <-- let's try to make it more mature
await personSancho.save({session})
personJanna.age = 100; // <-- let's try to make it more mature too
await personJanna.save({session})
personHulio.age = 100; // <-- let's try to make it more mature too
await personHulio.save({session})
throw new Error('Test an error - or remark me') // No changes will be saved
// return result - The result can be whatever you want
return personJanna
});
await transaction.commit();
```
If the scheme uses {timestamps: true} in the options, or the fields createdAt (if the document is new), updatedAt (for
new and updates) - they will be automatically created or updated updatedAt.
[](https://www.npmjs.com/package/mongoose)
[](https://nodejs.org)