How to implement Transactions in Objection.Js & Node.Js (Express)
Transaction is a way to execute or commit a group of operations as a unit. In other words, it is a technique to call multiple SQL statements as a single unit.
In case of the transaction if any error occurred all the operations rollback.
Objection.js is built on an SQL query builder called knex. Objection.js provides a very easy way to implement transactions.
# Without Transaction
const User = require("../models/user.model");
const ShippingAddress = require("../models/address.model");
async function register() {
try {
// Create User
const user = await User.query().insert({
name: 'Van Helsing'
});
// Create Shipping Address
const address = await ShippingAddress.query().insert({
address: 'Transylvania',
user_id: user.id
});
console.log('success');
} catch (error) {
console.log('error');
}
}
In the above example, we can face two error cases.
1. user creation operation is not executed.
2. user is created but the shipping address operation is not executed.
So to prevent this type of scenario we need to use transactions.
# Transactional Toolset
1. Creating a transaction
2. Rollback a transaction
3. Committing a transaction
# Implementation -
models/connection.js
const { Model } = require('objection');
const Knex = require('knex');
const knex = Knex({
client: 'mysql',
debug: false,
connection: {
database: 'my_db',
password: 'password',
user: 'username',
host: 'localhost',
},
})
Model.knex(knex);
module.exports = { knex }
1. Managed Transactions - Auto callback method
const { knex } = require("../models/connection");
const User = require("../models/user.model");
const ShippingAddress = require("../models/address.model");
async function register() {
try {
await knex.transaction(async function (transaction) {
// chain all your queries here. make sure you return them.
const user = await User.query(transaction).insert({
name: 'Van Helsing'
});
await ShippingAddress.query(transaction).insert({
address: 'Transylvania',
user_id: user.id
});
return user;
});
console.log('success');
} catch (error) {
console.log('error');
}
}
In this way, the transaction will automatically be committed. You don't need to worry about manually rolling back or committing while using the transaction method
2. Unmanaged Transactions - Manual method (Recommended)
In this way, we have more control over the operations. so I personally prefer this way to implement transactions. we have three methods for manually controlling the operations.
await knex.transaction(); // for starting a new transaction
await transaction.commit(); // for committing all operations
await transaction.rollback(); // for rollback the operations
const { knex } = require("../models/connection");
const User = require("../models/user.model");
const ShippingAddress = require("../models/address.model");
async function register() {
const transaction = await knex.transaction();
try {
const user = await User.query(transaction).insert({
name: 'Van Helsing'
});
await ShippingAddress.query(transaction).insert({
address: 'Transylvania',
user_id: user.id
});
console.log('success');
await transaction.commit();
} catch (error) {
console.log('error');
await transaction.rollback();
}
}
In the above example, we have full control over the operation so we can easily handle errors based on what error occurred.
#Extras:-
const { knex } = require("../models/connection");
async function example() {
const transaction = await knex.transaction();
try {
await Model.query(transaction).insert({ /* payload */ });
await Model.query(transaction).delete().where( /* conditions */ );
await Model.query(transaction).patch({ /* payload */ }).where( /* conditions */ );
await transaction.commit();
} catch (err) {
await transaction.rollback();
}
}
Leave Your Comment