In some cases, we may need to use a transaction. We should provide that feature by defining it in models.
Knex.js allows us to use transaction like the following code;
// Using trx as a transaction object:
const trx = await knex.transaction();
const books = [
{title: 'Canterbury Tales'},
{title: 'Moby Dick'},
{title: 'Hamlet'}
];
trx('catalogues')
.insert({name: 'Old Books'}, 'id')
.then(function(ids) {
books.forEach((book) => book.catalogue_id = ids[0]);
return trx('books').insert(books);
})
.then(trx.commit)
.catch(trx.rollback);
Configurations
Axe API has many shared codes between handlers. We may let the developers set their transaction strategy in the application general. But also, developers should be able to set special transaction rules for some handlers.
app/Config/Application.js
import { LOG_LEVEL } from "axe-api";
export default async () => {
return {
env: process.env.NODE_ENV,
port: process.env.APP_PORT,
logLevel: LOG_LEVEL.INFO,
transaction: false
};
};
Default transaction value: false
. But developers should be able to open in general. On the other hand, if a developer doesn't want a general transaction rule, they should be able to define a transaction rule in model definition;
import { Model } from "axe-api";
class User extends Model {
get transaction() {
return true;
}
}
export default User;
A transaction definition like this means that Axe API will use a transaction in all routes for the model. But, developers should be able to define handler-specific transactions too;
import { Model, HANDLERS } from "axe-api";
class User extends Model {
get transaction() {
return [
{
handler: HANDLERS.INSERT,
transaction: false
},
{
handler: HANDLERS.DELETE,
transaction: true
},
];
}
}
export default User;
Configuration importance is like this;
- If there is any handler-specific transaction configuration, use that option
- Else if there is any model-specific transaction configuration, use that option.
- Else use application-level transaction configuration.
Middlewares
Developers can't use transactions in middleware functions.
Events
In events, developers can't use transactions because events can have a different timeline.
Hooks
In hooks, developers should be able to use the transaction.
const onBeforeInsert = async ({ database, transaction }) => {
};
export { onBeforeInsert };
In general, we are passing the database instance to the hook methods. But, we should pass the transaction object to the hook methods if there is any transaction configuration for the related model. If the developers disabled the transaction, we should pass the transaction variable as NULL
.
Error Handling
We should be able to handle errors and we shouldn't let locked tables be.