Unique identifier of this transaction.
Attempts to abort the transaction to the databases.
Options for aborting the transaction.
const db = new Database();
const col = db.collection("some-collection");
const trx = db.beginTransaction(col);
await trx.step(() => col.save({ hello: "world" }));
const result = await trx.abort();
// result indicates the updated transaction status
Attempts to commit the transaction to the databases.
Options for comitting the transaction.
const db = new Database();
const col = db.collection("some-collection");
const trx = db.beginTransaction(col);
await trx.step(() => col.save({ hello: "world" }));
const result = await trx.commit();
// result indicates the updated transaction status
Retrieves general information about the transaction.
const db = new Database();
const col = db.collection("some-collection");
const trx = db.beginTransaction(col);
await trx.step(() => col.save({ hello: "world" }));
const info = await trx.get();
// the transaction exists
Executes the given function locally as a single step of the transaction.
Callback function returning a promise.
Warning: The callback function should wrap a single call of an async
arangojs method (e.g. a method on a Collection object of a collection
that is involved in the transaction or the db.query method).
If the callback function is async, only the first promise-returning (or
async) method call will be executed as part of the transaction. See the
examples below for how to avoid common mistakes when using this method.
Note: Avoid defining the callback as an async function if possible as arangojs will throw an error if the callback did not return a promise. Async functions will return an empty promise by default, making it harder to notice if you forgot to return something from the callback.
Note: Although almost anything can be wrapped in a callback and passed to this method, that does not guarantee ArangoDB can actually do it in a transaction. Refer to the ArangoDB documentation if you are unsure whether a given operation can be executed as part of a transaction. Generally any modification or retrieval of data is eligible but modifications of collections or databases are not.
const db = new Database();
const vertices = db.collection("vertices");
const edges = db.collection("edges");
const trx = await db.beginTransaction({ write: [vertices, edges] });
// The following code will be part of the transaction
const left = await trx.step(() => vertices.save({ label: "left" }));
const right = await trx.step(() => vertices.save({ label: "right" }));
// Results from preceding actions can be used normally
await trx.step(() => edges.save({
_from: left._id,
_to: right._id,
data: "potato"
}));
// Transaction must be committed for changes to take effected
// Always call either trx.commit or trx.abort to end a transaction
await trx.commit();
// BAD! If the callback is an async function it must only use await once!
await trx.step(async () => {
await collection.save(data);
await collection.save(moreData); // WRONG
});
// BAD! Callback function must use only one arangojs call!
await trx.step(() => {
return collection.save(data)
.then(() => collection.save(moreData)); // WRONG
});
// BETTER: Wrap every arangojs method call that should be part of the
// transaction in a separate `trx.step` call
await trx.step(() => collection.save(data));
await trx.step(() => collection.save(moreData));
// BAD! If the callback is an async function it must not await before
// calling an arangojs method!
await trx.step(async () => {
await doSomethingElse();
return collection.save(data); // WRONG
});
// BAD! Any arangojs inside the callback must not happen inside a promise
// method!
await trx.step(() => {
return doSomethingElse()
.then(() => collection.save(data)); // WRONG
});
// BETTER: Perform any async logic needed outside the `trx.step` call
await doSomethingElse();
await trx.step(() => collection.save(data));
// OKAY: You can perform async logic in the callback after the arangojs
// method call as long as it does not involve additional arangojs method
// calls, but this makes it easy to make mistakes later
await trx.step(async () => {
await collection.save(data);
await doSomethingDifferent(); // no arangojs method calls allowed
});
// BAD! The callback should not use any functions that themselves use any
// arangojs methods!
async function saveSomeData() {
await collection.save(data);
await collection.save(moreData);
}
await trx.step(() => saveSomeData()); // WRONG
// BETTER: Pass the transaction to functions that need to call arangojs
// methods inside a transaction
async function saveSomeData(trx) {
await trx.step(() => collection.save(data));
await trx.step(() => collection.save(moreData));
}
await saveSomeData(); // no `trx.step` call needed
// BAD! You must wait for the promise to resolve (or await on the
// `trx.step` call) before calling `trx.step` again!
trx.step(() => collection.save(data)); // WRONG
await trx.step(() => collection.save(moreData));
// BAD! The trx.step callback can not make multiple calls to async arangojs
// methods, not even using Promise.all!
await trx.step(() => Promise.all([ // WRONG
collection.save(data),
collection.save(moreData),
]));
// BAD! Multiple `trx.step` calls can not run in parallel!
await Promise.all([ // WRONG
trx.step(() => collection.save(data)),
trx.step(() => collection.save(moreData)),
]));
// BETTER: Always call `trx.step` sequentially, one after the other
await trx.step(() => collection.save(data));
await trx.step(() => collection.save(moreData));
// OKAY: The then callback can be used if async/await is not available
trx.step(() => collection.save(data))
.then(() => trx.step(() => collection.save(moreData)));
// BAD! The callback will return an empty promise that resolves before
// the inner arangojs method call has even talked to ArangoDB!
await trx.step(async () => {
collection.save(data); // WRONG
});
// BETTER: Use an arrow function so you don't forget to return
await trx.step(() => collection.save(data));
// OKAY: Remember to always return when using a function body
await trx.step(() => {
return collection.save(data); // easy to forget!
});
// OKAY: You do not have to use arrow functions but it helps
await trx.step(function () {
return collection.save(data);
});
// BAD! You can not pass promises instead of a callback!
await trx.step(collection.save(data)); // WRONG
// BETTER: Wrap the code in a function and pass the function instead
await trx.step(() => collection.save(data));
// WORSE: Calls to non-async arangojs methods don't need to be performed
// as part of a transaction
const collection = await trx.step(() => db.collection("my-documents"));
// BETTER: If an arangojs method is not async and doesn't return promises,
// call it without `trx.step`
const collection = db.collection("my-documents");
Executes the given function as an asynchronous step of the transaction.
Unlike Transaction#step, the transaction context remains active
until the Promise returned by callback settles. This allows the callback
to perform asynchronous work before an arangojs request and to make
multiple sequential arangojs requests across await boundaries.
On Node.js, different transactions can use this method concurrently on the same databases.Database. In browsers, only one asynchronous step may be active per connection at a time. Steps belonging to the same server transaction should always be awaited sequentially.
The callback must return all work that should remain in the transaction. Unreturned or fire-and-forget Promises may outlive the transaction context.
const trx = await db.beginTransaction(collection);
await trx.stepAsync(async () => {
await loadDataFromExternalApi();
await collection.save({ _key: "first" });
return collection.save({ _key: "second" });
});
await trx.commit();
Represents a streaming transaction in a databases.Database.