Crate axum_sqlx_tx
source ·Expand description
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 Layer
to your app:
let pool = /* any sqlx::Pool */
let app = axum::Router::new()
// .route(...)s
.layer(axum_sqlx_tx::Layer::new(pool));
You can then simply add Tx
as an argument to your handlers:
use axum_sqlx_tx::Tx;
use sqlx::Sqlite;
async fn create_user(mut tx: Tx<Sqlite>, /* ... */) {
// `&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();
/* ... */
}
If you forget to add the middleware you’ll get Error::MissingExtension
(internal server
error) when using the extractor. You’ll also get an error (Error::OverlappingExtractors
) if
you have multiple Tx
arguments in a single handler, or call Tx::from_request
multiple times
in a single middleware.
Error handling
axum
requires that middleware do not return errors, and that the errors returned by extractors
implement IntoResponse
. By default, Error
is used by Layer
and Tx
to
convert errors into HTTP 500 responses, with the error’s Display
value as the response body,
however it’s generally not a good practice to return internal error details to clients!
To make it easier to customise error handling, both Layer
and Tx
have a second generic
type parameter, E
, that can be used to override the error type that will be used to convert
the response.
use axum::response::IntoResponse;
use axum_sqlx_tx::Tx;
use sqlx::Sqlite;
struct MyError(axum_sqlx_tx::Error);
// Errors must implement From<axum_sqlx_tx::Error>
impl From<axum_sqlx_tx::Error> for MyError {
fn from(error: axum_sqlx_tx::Error) -> Self {
Self(error)
}
}
// Errors must implement IntoResponse
impl IntoResponse for MyError {
fn into_response(self) -> axum::response::Response {
// note that you would probably want to log the error or something
(http::StatusCode::INTERNAL_SERVER_ERROR, "internal server error").into_response()
}
}
// Change the layer error type
let app = axum::Router::new()
// .route(...)s
.layer(axum_sqlx_tx::Layer::new_with_error::<MyError>(pool));
// Change the extractor error type
async fn create_user(mut tx: Tx<Sqlite, MyError>, /* ... */) {
/* ... */
}
Examples
See examples/
in the repo for more examples.
Structs
tower_layer::Layer
that enables the Tx
extractor.tower_service::Service
that enables the Tx
extractor.axum
extractor for a database transaction.