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
173 downloads per month
Used in aide-axum-sqlx-tx
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::Transaction
s) 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