0
# Transactions
1
2
Database transaction management with model binding and automatic rollback.
3
4
## Capabilities
5
6
### Transaction Function
7
8
Create and manage database transactions with automatic rollback on errors.
9
10
```javascript { .api }
11
/**
12
* Execute callback within a database transaction
13
* @param callback - Function to execute within transaction
14
* @returns Promise resolving to callback result
15
*/
16
function transaction<T>(
17
callback: (trx: Transaction) => Promise<T>
18
): Promise<T>;
19
20
/**
21
* Execute callback within a transaction using specific Knex instance
22
* @param knex - Knex instance to use for transaction
23
* @param callback - Function to execute within transaction
24
* @returns Promise resolving to callback result
25
*/
26
function transaction<T>(
27
knex: Knex,
28
callback: (trx: Transaction) => Promise<T>
29
): Promise<T>;
30
31
/**
32
* Execute callback with bound model classes
33
* @param modelClass - Model class to bind to transaction
34
* @param callback - Function receiving bound model class
35
* @returns Promise resolving to callback result
36
*/
37
function transaction<T>(
38
modelClass: typeof Model,
39
callback: (BoundModel: typeof Model, trx?: Transaction) => Promise<T>
40
): Promise<T>;
41
42
/**
43
* Execute callback with multiple bound model classes
44
*/
45
function transaction<T>(
46
modelClass1: typeof Model,
47
modelClass2: typeof Model,
48
callback: (BoundModel1: typeof Model, BoundModel2: typeof Model, trx?: Transaction) => Promise<T>
49
): Promise<T>;
50
```
51
52
**Usage Examples:**
53
54
```javascript
55
const { transaction, Model } = require('objection');
56
57
// Basic transaction
58
const result = await transaction(async (trx) => {
59
const person = await Person.query(trx)
60
.insert({ firstName: 'John', lastName: 'Doe' });
61
62
const pet = await Pet.query(trx)
63
.insert({ name: 'Fluffy', ownerId: person.id });
64
65
return { person, pet };
66
});
67
68
// Transaction with specific Knex instance
69
const result = await transaction(knex, async (trx) => {
70
// Operations using the provided knex instance
71
const person = await Person.query(trx).insert(personData);
72
return person;
73
});
74
75
// Transaction with bound model classes
76
const result = await transaction(Person, Pet, async (BoundPerson, BoundPet, trx) => {
77
// BoundPerson and BoundPet are automatically bound to the transaction
78
const person = await BoundPerson.query().insert(personData);
79
const pet = await BoundPet.query().insert({ ...petData, ownerId: person.id });
80
81
return { person, pet };
82
});
83
```
84
85
### Model Transaction Methods
86
87
Transaction-related methods available on model classes.
88
89
```javascript { .api }
90
/**
91
* Start a new transaction
92
* @param knexOrTransaction - Knex instance or existing transaction
93
* @returns Promise resolving to Transaction
94
*/
95
static startTransaction(knexOrTransaction?: Knex | Transaction): Promise<Transaction>;
96
97
/**
98
* Execute callback within a transaction
99
* @param callback - Function to execute
100
* @returns Promise resolving to callback result
101
*/
102
static transaction<T>(callback: (trx: Transaction) => Promise<T>): Promise<T>;
103
104
/**
105
* Execute callback within a transaction with specific Knex/transaction
106
* @param trxOrKnex - Transaction or Knex instance
107
* @param callback - Function to execute
108
* @returns Promise resolving to callback result
109
*/
110
static transaction<T>(
111
trxOrKnex: Transaction | Knex,
112
callback: (trx: Transaction) => Promise<T>
113
): Promise<T>;
114
115
/**
116
* Bind model class to a Knex instance or transaction
117
* @param trxOrKnex - Transaction or Knex instance to bind
118
* @returns Bound model class
119
*/
120
static bindKnex(trxOrKnex: Transaction | Knex): typeof Model;
121
122
/**
123
* Bind model class to a transaction (alias for bindKnex)
124
* @param trxOrKnex - Transaction or Knex instance to bind
125
* @returns Bound model class
126
*/
127
static bindTransaction(trxOrKnex: Transaction | Knex): typeof Model;
128
```
129
130
**Usage Examples:**
131
132
```javascript
133
// Start transaction manually
134
const trx = await Person.startTransaction();
135
try {
136
const person = await Person.query(trx).insert(personData);
137
const pet = await Pet.query(trx).insert(petData);
138
139
await trx.commit();
140
return { person, pet };
141
} catch (error) {
142
await trx.rollback();
143
throw error;
144
}
145
146
// Model class transaction
147
const result = await Person.transaction(async (trx) => {
148
const person = await Person.query(trx).insert(personData);
149
return person;
150
});
151
152
// Bind model to transaction
153
const trx = await Person.startTransaction();
154
const BoundPerson = Person.bindKnex(trx);
155
156
try {
157
const person = await BoundPerson.query().insert(personData);
158
await trx.commit();
159
} catch (error) {
160
await trx.rollback();
161
throw error;
162
}
163
```
164
165
### Using Transactions with Queries
166
167
Pass transactions to query methods for transaction-aware operations.
168
169
```javascript { .api }
170
/**
171
* Execute query within a transaction
172
* @param trx - Transaction instance
173
* @returns QueryBuilder bound to transaction
174
*/
175
query(trx: Transaction): QueryBuilder;
176
177
/**
178
* Execute related query within a transaction
179
* @param relationName - Relation name
180
* @param trx - Transaction instance
181
* @returns QueryBuilder bound to transaction
182
*/
183
relatedQuery(relationName: string, trx: Transaction): QueryBuilder;
184
```
185
186
**Usage Examples:**
187
188
```javascript
189
await transaction(async (trx) => {
190
// All queries use the same transaction
191
const person = await Person.query(trx)
192
.insert({ firstName: 'John', lastName: 'Doe' });
193
194
const pets = await person.$relatedQuery('pets', trx)
195
.insert([
196
{ name: 'Fluffy', species: 'cat' },
197
{ name: 'Buddy', species: 'dog' }
198
]);
199
200
// Update within transaction
201
await Person.query(trx)
202
.findById(person.id)
203
.patch({ petCount: pets.length });
204
});
205
```
206
207
### Transaction with Graph Operations
208
209
Graph operations automatically participate in transactions.
210
211
**Usage Examples:**
212
213
```javascript
214
await transaction(async (trx) => {
215
// insertGraph uses the transaction
216
const result = await Person.query(trx)
217
.insertGraphAndFetch({
218
firstName: 'Jane',
219
lastName: 'Smith',
220
pets: [
221
{ name: 'Max', species: 'dog' },
222
{ name: 'Luna', species: 'cat' }
223
],
224
movies: [
225
{ title: 'New Movie', year: 2023 }
226
]
227
});
228
229
// All inserts are in the same transaction
230
return result;
231
});
232
```
233
234
### Transaction Error Handling
235
236
Transactions automatically rollback on errors, but you can also handle them explicitly.
237
238
**Usage Examples:**
239
240
```javascript
241
const { DBError, ValidationError } = require('objection');
242
243
try {
244
await transaction(async (trx) => {
245
// If any operation fails, transaction is automatically rolled back
246
const person = await Person.query(trx).insert(personData);
247
const pet = await Pet.query(trx).insert(invalidPetData); // Might fail
248
249
return { person, pet };
250
});
251
} catch (error) {
252
if (error instanceof ValidationError) {
253
console.log('Validation failed:', error.data);
254
} else if (error instanceof DBError) {
255
console.log('Database error:', error.message);
256
}
257
// Transaction was automatically rolled back
258
}
259
```
260
261
### Nested Transactions
262
263
Handle nested transaction calls appropriately.
264
265
**Usage Examples:**
266
267
```javascript
268
await transaction(async (trx) => {
269
const person = await Person.query(trx).insert(personData);
270
271
// Nested transaction - will reuse outer transaction
272
const pets = await transaction(trx, async (innerTrx) => {
273
return Pet.query(innerTrx).insert(petDataArray);
274
});
275
276
return { person, pets };
277
});
278
```
279
280
### Transaction Isolation Levels
281
282
Control transaction isolation levels through Knex configuration.
283
284
**Usage Examples:**
285
286
```javascript
287
// Configure isolation level in knex config
288
const knex = Knex({
289
client: 'postgresql',
290
connection: connectionConfig,
291
pool: {
292
afterCreate: function(conn, done) {
293
conn.query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;', done);
294
}
295
}
296
});
297
298
// Or set for specific transaction
299
await transaction(async (trx) => {
300
await trx.raw('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
301
// Perform operations...
302
});
303
```
304
305
### Transaction Savepoints
306
307
Use savepoints for partial rollbacks within transactions.
308
309
**Usage Examples:**
310
311
```javascript
312
await transaction(async (trx) => {
313
const person = await Person.query(trx).insert(personData);
314
315
const savepoint = await trx.savepoint();
316
317
try {
318
// Risky operation
319
await Pet.query(trx).insert(riskyPetData);
320
await savepoint.release();
321
} catch (error) {
322
// Rollback to savepoint, not entire transaction
323
await savepoint.rollback();
324
console.log('Pet creation failed, but person creation preserved');
325
}
326
327
return person;
328
});
329
```
330
331
## Types
332
333
```typescript { .api }
334
type Transaction = Knex.Transaction;
335
336
interface TransactionConfig {
337
isolationLevel?: 'read uncommitted' | 'read committed' | 'repeatable read' | 'serializable';
338
readOnly?: boolean;
339
}
340
341
interface Savepoint {
342
release(): Promise<void>;
343
rollback(): Promise<void>;
344
}
345
346
type TransactionCallback<T> = (trx: Transaction) => Promise<T>;
347
348
type ModelTransactionCallback<T> = (
349
BoundModel: typeof Model,
350
trx?: Transaction
351
) => Promise<T>;
352
```