Reputation: 739
I'm starting with sequelize to create an API and I'm facing an issue with transactions.
My sequelize database configuration looks like this:
var Sequelize = require('sequelize');
var sequelize = new Sequelize(CONFIG.database, env.user,
env.password, {
host: env.host,
dialect: env.dialect,
port: env.port,
operatorsAliases: false
});
var db = {};
fs.readdirSync(__dirname).filter(function (file) {
return (file.indexOf('.') !== 0) && (file !== 'index.js');
}).forEach(function (file) {
var model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(function (modelName) {
if ('associate' in db[modelName]) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
Then I have a stockcontroller with functions to save in database like this:
var exports = module.exports = {}
let Stock = require('../models').Stock;
let StockVariant = require('../models').StockVariant;
exports.create = function (req, res) {
const body = req.body;
console.log(body);
Stock.create(body).then(function (stock, created) {})...}
I want to create transaction to save into stockvariant and stock tables in one single transaction and have the option to rollback when error.
Documentation in sequelize doesn't look easy for me to understand as I don't see how to apply this
return sequelize.transaction(function (t) { return User.create({}) })
because t is of course not defined anywhere and my stockcontroller doesn't import sequelize.
So in the end I don't understand the basic concept of how to define that transaction function to create a new stock line.
Thanks for your help!
Upvotes: 2
Views: 8760
Reputation: 1205
The sequelize instance needs to be imported to use transactions. It is already exported in your database configuration file with this line db.sequelize = sequelize
.
All you need to do is adding it in the current imports :
var exports = module.exports = {}
const Stock = require('../models').Stock; // Prefer const usage to avoid overwritting imports
const StockVariant = require('../models').StockVariant;
const sequelize = require('../models').sequelize;
This could also be done in one line using destructuring :
const { Stock, StockVariant, sequelize } = require('../models');
Now let's come to the transaction. As stated in the documentation, you have two ways of handling them : managed or unmanaged.
Managed transaction
This is done by chaining your asynchronous operations inside the sequelize transaction callback. In this case, if the operations linked to the transaction succeed, the transaction will commit automatically, otherwise it will rollback.
exports.create = function (req, res) {
const body = req.body;
console.log(body);
sequelize.transaction(function(t) {
return Stock.create(body, {transaction: t}) // We pass the transaction as a parameter here !
.then(function(stock, created) {
return StockVariant.create(..., {transaction: t}) // The transaction, again here
})
.catch(function(err) {
// Handle your error...
});
}
Unmanaged transaction
If you want more transparency and/or control over your transaction, you can use an unmanaged transaction. In this case, you must call commit
and rollback
manually.
exports.create = function (req, res) {
const body = req.body;
console.log(body);
sequelize.transaction
.then(function(t) { // Note the 'then' usage here
return Stock.create(body, {transaction: t}); // We pass the transaction as a parameter here !
.then(function(stock, created) {
return StockVariant.create(..., {transaction: t}); // The transaction, again here
});
.then(function() {
return t.commit();
})
.catch(function(err) {
return t.rollback();
});
}
This could also be done with async / await
syntax, which may be more pleasant to read :
exports.create = function (req, res) {
const body = req.body;
console.log(body);
let t; // The variable in which the transaction object will be stored
try {
t = await sequelize.transaction();
const stock = await Stock.create(body, {transaction: t})
await StockVariant.create(..., {transaction: t}) // Whatever parameter you need to pass here
await t.commit();
} catch (err) {
await t.rollback();
}
}
Upvotes: 4