Transactions
Running a Transaction
Start a transaction with Kysely's db.transaction().execute() (or the Model.transaction shorthand) and pass the transaction object to the model operations that should run inside it. The transaction commits when the callback resolves and rolls back if it throws.
More documentation on Kysely transactions is available in the Kysely docs.
import db from "./database/db";
await db.transaction().execute(async (trx) => {
const person = await Person.create({ name: "Aang", birthday: new Date("1993-01-01") }, trx);
const pet = new Pet({ name: "Appa", type: "bison", person_id: person.id });
await pet.save(trx);
});
Queries
Pass the transaction to query() to run a query on it. Everything chained from the builder runs on the transaction.
await db.transaction().execute(async (trx) => {
const pets = await Pet.query(trx).where("type", "cat").get();
const owner = await Person.query(trx).find(1);
});
You can also bind a connection to an existing builder chain with useConnection.
const query = Pet.query().where("type", "cat");
const pets = await query.useConnection(trx).get();
Models Remember Their Connection
Models loaded through a transaction-bound query keep that connection. Calling save, delete, or accessing relations on them stays inside the transaction. You don't need to pass the transaction again for subsequent operations.
await db.transaction().execute(async (trx) => {
const pet = await Pet.query(trx).firstOrFail();
pet.name = "Momo";
await pet.save(); // runs on trx
const owner = await pet.owner; // loaded through trx
});
The same applies to models returned by create(attributes, trx) and to eager-loaded relations (Pet.query(trx).with("owner")).
For a model instance that isn't already bound to a transaction, either pass the transaction to save/delete directly or bind it with useConnection:
const pet = new Pet({ name: "Hawky", type: "hawk" });
await pet.save(trx);
// or
await pet.useConnection(trx).save();
Creating
create and createMany accept a connection as their second argument.
await db.transaction().execute(async (trx) => {
await Pet.create({ name: "Momo", type: "lemur" }, trx);
await Pet.create(
[
{ name: "Appa", type: "bison" },
{ name: "Hawky", type: "hawk" },
],
trx,
);
});
Model.transaction
As a shorthand, you can start a transaction directly from any model without importing the database instance. The transaction runs on that model's database and can be used with any model on the same database.
await Person.transaction(async (trx) => {
const person = await Person.create({ name: "Katara" }, trx);
await Pet.create({ name: "Momo", type: "lemur", person_id: person.id }, trx);
});
Raw Kysely Queries
The transaction is a regular Kysely Transaction, so raw queries can run on it alongside model activity.
await db.transaction().execute(async (trx) => {
await Person.create({ name: "Toph" }, trx);
await trx.updateTable("people").set({ favorite_color: "green" }).where("name", "=", "Toph").execute();
});