12 releases (breaking)

0.9.0 Jul 26, 2024
0.8.0 Dec 23, 2023
0.7.0 Dec 1, 2023
0.6.0 Sep 15, 2023
0.2.0 Mar 4, 2022

#1063 in Database interfaces

Download history 28/week @ 2024-08-09 43/week @ 2024-08-16 16/week @ 2024-08-23 14/week @ 2024-08-30 12/week @ 2024-09-06 31/week @ 2024-09-13 119/week @ 2024-09-20 100/week @ 2024-09-27 50/week @ 2024-10-04 52/week @ 2024-10-11 20/week @ 2024-10-18 6/week @ 2024-10-25 42/week @ 2024-11-01 24/week @ 2024-11-08 50/week @ 2024-11-15 56/week @ 2024-11-22

173 downloads per month
Used in aide-axum-sqlx-tx

MIT license

31KB
441 lines

axum-sqlx-tx

Request-bound SQLx transactions for axum.

Summary

axum-sqlx-tx provides an axum extractor for obtaining a request-bound transaction. The transaction begins the first time the extractor is used, and is stored with the request for use by other middleware/handlers. The transaction is resolved depending on the status code of the response – successful (2XX) responses will commit the transaction, otherwise it will be rolled back.

See the crate documentation for more information and examples.


lib.rs:

Request-bound SQLx transactions for axum.

[Tx] is an axum extractor for obtaining a transaction that's bound to the HTTP request. A transaction begins the first time the extractor is used for a request, and is then stored in request extensions for use by other middleware/handlers. The transaction is resolved depending on the status code of the eventual response – successful (HTTP 2XX or 3XX) responses will cause the transaction to be committed, otherwise it will be rolled back.

This behaviour is often a sensible default, and using the extractor (e.g. rather than directly using sqlx::Transactions) means you can't forget to commit the transactions!

Usage

To use the [Tx] extractor, you must first add State and Layer to your app. State holds the configuration for the extractor, and the Layer middleware manages the request-bound transaction.

// It's recommended to create aliases specialised for your extractor(s)
type Tx = axum_sqlx_tx::Tx<sqlx::Sqlite>;

let pool = sqlx::SqlitePool::connect("...").await.unwrap();

let (state, layer) = Tx::setup(pool);

let app = axum::Router::new()
    // .route(...)s
    .layer(layer)
    .with_state(state);

You can then simply add [Tx] as an argument to your handlers:

type Tx = axum_sqlx_tx::Tx<sqlx::Sqlite>;

async fn create_user(mut tx: Tx, /* ... */) {
    // `&mut Tx` implements `sqlx::Executor`
    let user = sqlx::query("INSERT INTO users (...) VALUES (...)")
        .fetch_one(&mut tx)
        .await
        .unwrap();

    // `Tx` also implements `Deref<Target = sqlx::Transaction>` and `DerefMut`
    use sqlx::Acquire;
    let inner = tx.begin().await.unwrap();
    /* ... */
}

Error handling

axum requires that errors can be turned into responses. The Error type converts into a HTTP 500 response with the error message as the response body. This may be suitable for development or internal services but it's generally not advisable to return internal error details to clients.

See Error for how to customise error handling.

Multiple databases

If you need to work with multiple databases, you can define marker structs for each. See Marker for an example.

It's not currently possible to use Tx for a dynamic number of databases. Feel free to open an issue if you have a requirement for this.

Accessing the pool

Note that State implements FromRef into the inner SQLx pool. Therefore, if you still need to access the database pool at some handler, you can use axum's State extractor normally.

use axum::extract::State;

async fn this_still_works(State(pool): State<sqlx::SqlitePool>) {
    /* ... */
}

Examples

See examples/ in the repo for more examples.

Dependencies

~9–16MB
~198K SLoC