# Manual transactions

Use SurrealQL BEGIN and COMMIT in queries, or the Rust SDK `begin` / `commit` / `cancel` transaction handle, and check per-statement results before committing

**3.x**

While every query in SurrealDB is run [inside its own transaction](/docs/reference/query-language/language-primitives/transactions.md), manual transactions made up of multiple statements can be used via the [BEGIN](/docs/reference/query-language/statements/begin.md) and [COMMIT](/docs/reference/query-language/statements/commit.md) keywords.

## Getting started

Start a running database using the following command:

```bash
surreal start --user root --pass secret 
```

To follow along interactively, connect [using SurrealDB Studio](/docs/explore/studio.md) or the following command to open a connection in the CLI:

```bash
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:

```toml
[dependencies]
surrealdb = "3.2.0"
tokio = "1.52.1"
```

### Using a client-side transaction

Once this is done, you can use [`.begin()`](https://docs.rs/surrealdb/latest/surrealdb/struct.Surreal.html#method.begin) to get a client-side [`Transaction`](https://docs.rs/surrealdb/latest/surrealdb/method/struct.Transaction.html). Run your statements with [`.query()`](https://docs.rs/surrealdb/latest/surrealdb/method/struct.Transaction.html#method.query) or other methods such as `.select()`,` `.create()` and so on, then end the transaction in one of two ways:

* [`commit()`](https://docs.rs/surrealdb/latest/surrealdb/method/struct.Transaction.html#method.commit) - apply the changes. The future resolves to a [`Surreal`](https://docs.rs/surrealdb/latest/surrealdb/struct.Surreal.html) client again.
* [`cancel()`](https://docs.rs/surrealdb/latest/surrealdb/method/struct.Transaction.html#method.cancel) - roll back. This also returns the `Surreal` client when it completes.

Note that the outer `Result` from `await`ing 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()`](https://docs.rs/surrealdb/latest/surrealdb/struct.IndexedResults.html#method.take_errors) on the query response, or use [`check()`](https://docs.rs/surrealdb/latest/surrealdb/struct.IndexedResults.html#method.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.

```rust
use surrealdb::Surreal;
use surrealdb::engine::any::{connect, Any};

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
    let db = connect("memory").await?;
    db.use_ns("ns").use_db("db").await?;

    let db = run_in_transaction(db, "LET $x: int = 'not a number';").await?;
    let db = 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.
async fn run_in_transaction(
    db: Surreal<Any>,
    surql: &str,
) -> surrealdb::Result<Surreal<Any>> {
    let tx = db.begin().await?;

    match tx.query(surql).await {
        Ok(mut response) => {
            let errors = response.take_errors();
            if !errors.is_empty() {
                eprintln!("Errors from statements: {errors:#?}\n");
                return tx.cancel().await;
            }
            println!("Ok: {response:#?}\n");
            return tx.commit().await;
        }
        Err(e) => {
            eprintln!("Error from query request: {e}\n");
            return tx.cancel().await;
        }
    }
}
```

### Using SurrealQL transaction statements

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.

```rust
use surrealdb::Surreal;
use surrealdb::engine::remote::ws::Ws;
use surrealdb::opt::auth::Root;
use surrealdb::types::{SurrealValue, ToSql, Value};

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
    let db = connect("memory").await?;

    db.signin(Root {
        username: "root".to_string(),
        password: "secret".to_string(),
    })
    .await?;

    db.use_ns("main").use_db("main").await?;

    let mut response = db
        .query(
            "
        // Start transaction
        BEGIN;
        // Setup accounts
        CREATE account:one SET balance = 135605.16;
        CREATE account:two SET balance = 91031.31;
        // Move money
        UPDATE account:one SET balance += 300.00;
        UPDATE account:two SET balance -= 300.00;
        // Finalise
        COMMIT;
        ",
        )
        .await?;

    for i in 0..response.num_statements() {
        println!(
            "{}",
            response
                .take::<Option<Value>>(i)
                .unwrap()
                .into_value()
                .to_sql()
        );
    }

    Ok(())
}
```

The output will look like this.

```text
NONE
[{ balance: 135605.16f, id: account:one }]
[{ balance: 91031.31f, id: account:two }]
[{ balance: 135905.16f, id: account:one }]
[{ balance: 90731.31f, id: account:two }]
NONE
```

To avoid the possibility of typos, the [`.set()`](/docs/reference/rust/methods/set.md) method can be used to set the amount to transfer.

```rust
use surrealdb::Surreal;
use surrealdb::engine::remote::ws::Ws;
use surrealdb::opt::auth::Root;

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
    let db = Surreal::new::<Ws>("localhost:8000").await?;

    db.signin(Root {
        username: "root".to_string(),
        password: "secret".to_string(),
    })
    .await?;

    db.use_ns("main").use_db("main").await?;

    // Set the parameter $amount for later use
    db.set("amount", 300).await?;

    let response = db
        .query(
            "
        // Start transaction
        BEGIN;
        // Setup accounts
        CREATE account:one SET balance = 135605.16;
        CREATE account:two SET balance = 91031.31;
        // Move money
        UPDATE account:one SET balance += $amount;
        UPDATE account:two SET balance -= $amount;
        // Finalise
        COMMIT;
        ",
        )
        .await?;

    println!("{response:#?}");

	// See if any errors were returned
	response.check()?;

	Ok(())
}
```

**2.x**

## Manual transactions

While every query in SurrealDB is run [inside its own transaction](/docs/reference/query-language/language-primitives/transactions.md), manual transactions made up of multiple statements can be used via the [BEGIN](/docs/reference/query-language/statements/begin.md) and [COMMIT](/docs/reference/query-language/statements/commit.md) keywords.

The [`.query()`](https://docs.rs/surrealdb/latest/surrealdb/struct.Surreal.html#method.query) method can take any number of statements, returning a [`Response`](https://docs.rs/surrealdb/latest/surrealdb/struct.IndexedResults.html) that contains the results of each of them. In addition, the same method before being called returns [a struct](https://docs.rs/surrealdb/latest/surrealdb/method/struct.Query.html) that also allows [the same `.query()` method](https://docs.rs/surrealdb/latest/surrealdb/method/struct.Query.html#method.query) 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:

```bash
surreal start --user root --pass secret 
```

To follow along interactively, connect [using SurrealDB Studio](/docs/explore/studio.md) or the following command to open a connection in the CLI:

```bash
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:

```toml
[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.

```rust
use surrealdb::engine::remote::ws::Ws;
use surrealdb::opt::auth::Root;
use surrealdb::Surreal;

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
	let db = Surreal::new::<Ws>("localhost:8000").await?;

	db.signin(Root {
		username: "root",
		password: "secret",
	})
	.await?;

	db.use_ns("main").use_db("main").await?;

    let response = db
        .query("
        // Start transaction
        BEGIN;
        // Setup accounts
        CREATE account:one SET balance = 135605.16;
        CREATE account:two SET balance = 91031.31;
        // Move money
        UPDATE account:one SET balance += 300.00;
        UPDATE account:two SET balance -= 300.00;
        // Finalise
        COMMIT;
        ")
        .await?;

    println!("{response:#?}");

	// See if any errors were returned
	response.check()?;

	Ok(())
}
```

To avoid the possibility of typos, the [`.set()`](/docs/reference/rust/methods/set.md) method can be used to set the amount to transfer.

```rust
use surrealdb::engine::remote::ws::Ws;
use surrealdb::opt::auth::Root;
use surrealdb::Surreal;

#[tokio::main]
async fn main() -> surrealdb::Result<()> {
	let db = Surreal::new::<Ws>("localhost:8000").await?;

	db.signin(Root {
		username: "root",
		password: "secret",
	})
	.await?;

	db.use_ns("main").use_db("main").await?;

    // Set the parameter $amount for later use
    db.set("amount", 300).await?;

    let response = db
        .query("
        // Start transaction
        BEGIN;
        // Setup accounts
        CREATE account:one SET balance = 135605.16;
        CREATE account:two SET balance = 91031.31;
        // Move money
        UPDATE account:one SET balance += $amount;
        UPDATE account:two SET balance -= $amount;
        // Finalise
        COMMIT;
        ")
        .await?;

    println!("{response:#?}");

	// See if any errors were returned
	response.check()?;

	Ok(())
}
```

`Surreal::begin()` and the `Transaction` handle (with [`commit()`](https://docs.rs/surrealdb/3.0.5/surrealdb/method/struct.Transaction.html#method.commit) / [`cancel()`](https://docs.rs/surrealdb/3.0.5/surrealdb/method/struct.Transaction.html#method.cancel)) are only in the `surrealdb` **Rust 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](https://crates.io/crates/surrealdb) and add `surrealdb = "3.0.5"` (or similar) in `Cargo.toml` to use that API.
