Use SurrealQL BEGIN and COMMIT in queries, or the Rust SDK `begin` / `commit` / `cancel` transaction handle, and check per-statement results before committing
Manual transactions
While every query in SurrealDB is run inside its own transaction, manual transactions made up of multiple statements can be used via the BEGIN and COMMIT keywords.
Getting started
Start a running database using the following command:
surreal start --user root --pass secret
To follow along interactively, connect using Surrealist or the following command to open a connection in the CLI:
surreal sql --user root --pass secret --pretty
Then use the cargo add command to add the surrealdb and tokio crates. The dependencies inside Cargo.toml should look something like this:
[dependencies] surrealdb = "3.0.5" tokio = "1.52.1"
Using a client-side transaction
Once this is done, you can use .begin() to get a client-side Transaction. Run your statements with .query() or other methods such as .select(),.create()` and so on, then end the transaction in one of two ways:
commit() — apply the changes. The future resolves to a Surreal client again.
cancel() — roll back. This also returns the Surreal client when it completes.
Note that the outer Result from awaiting a query only shows that the statements have succeeded, but a response can still include per-statement failures (the request succeeded, but one of the SQL statements did not). Collect those with take_errors() on the query response, or use check() to fail on the first error. If the outer Result is Err, the transaction is not usable as intended.
The following example uses an in-memory database, runs every query in its own short transaction, and cancels (or would skip a commit) when a statement error shows up.
letdb=run_in_transaction(db,"LET $x: int = 'not a number';").await?; letdb=run_in_transaction(db,"SELECT SELECT SELECT").await?; run_in_transaction(db,"9").await?;
Ok(()) }
// Runs a single query inside a new transaction. // `commit` only runs when there // are no per-statement errors; // otherwise the transaction is cancelled. asyncfnrun_in_transaction( db: Surreal<Any>, surql: &str, )->surrealdb::Result<Surreal<Any>> { lettx=db.begin().await?;
A manual transaction can also be performed by sending in a BEGIN and other statements into the .query() method manually. This will result in the same behaviour as the previous method, but will not return a Transaction on the SDK side.
// See if any errors were returned response.check()?;
Ok(()) }
Manual transactions
While every query in SurrealDB is run inside its own transaction, manual transactions made up of multiple statements can be used via the BEGIN and COMMIT keywords.
The .query() method can take any number of statements, returning a Response that contains the results of each of them. In addition, the same method before being called returns a struct that also allows the same .query() method to be called on it, chaining the new query onto the existing query. This can help greatly with readability, as the example code below shows.
Getting started
Start a running database using the following command:
surreal start --user root --pass secret
To follow along interactively, connect using Surrealist or the following command to open a connection in the CLI:
surreal sql --user root --pass secret --ns main --db main --pretty
Then use the cargo add command to add the surrealdb and tokio crates. The dependencies inside Cargo.toml should look something like this:
[dependencies] surrealdb = "2.4.1" tokio = "1.49.0"
Once this is done, copy and paste the following code to run a manual transaction that creates two account records and then transfers 300 units from one account to the other.
// See if any errors were returned response.check()?;
Ok(()) }
Surreal::begin() and the Transaction handle (with commit() / cancel()) are only in the surrealdbRust crate 3.0.0+. The first tab (3.x) shows the full example, including per-statement errors and commit or cancel. In this 2.x tab, use BEGIN / COMMIT in SurrealQL, or upgrade the crate to 3.0 or newer and add surrealdb = "3.0.5" (or similar) in Cargo.toml to use that API.