|
| 1 | +/// Possible errors when extracting [`Tx`] from a request. |
| 2 | +/// |
| 3 | +/// Errors can occur at two points during the request lifecycle: |
| 4 | +/// |
| 5 | +/// 1. The [`Tx`] extractor might fail to obtain a connection from the pool and `BEGIN` a |
| 6 | +/// transaction. This could be due to: |
| 7 | +/// |
| 8 | +/// - Forgetting to add the middleware: [`Error::MissingExtension`]. |
| 9 | +/// - Calling the extractor multiple times in the same request: [`Error::OverlappingExtractors`]. |
| 10 | +/// - A problem communicating with the database: [`Error::Database`]. |
| 11 | +/// |
| 12 | +/// 2. The middleware [`Layer`] might fail to commit the transaction. This could be due to a problem |
| 13 | +/// communicating with the database, or else a logic error (e.g. unsatisfied deferred |
| 14 | +/// constraint): [`Error::Database`]. |
| 15 | +/// |
| 16 | +/// `axum` requires that errors can be turned into responses. The [`Error`] type converts into a |
| 17 | +/// HTTP 500 response with the error message as the response body. This may be suitable for |
| 18 | +/// development or internal services but it's generally not advisable to return internal error |
| 19 | +/// details to clients. |
| 20 | +/// |
| 21 | +/// You can override the error types for both the [`Tx`] extractor and [`Layer`]: |
| 22 | +/// |
| 23 | +/// - Override the [`Tx`]`<DB, E>` error type using the `E` generic type parameter. `E` must be |
| 24 | +/// convertible from [`Error`] (e.g. [`Error`]`: Into<E>`). |
| 25 | +/// |
| 26 | +/// - Override the [`Layer`] error type using [`Config::layer_error`](crate::Config::layer_error). |
| 27 | +/// The layer error type must be convertible from `sqlx::Error` (e.g. |
| 28 | +/// `sqlx::Error: Into<LayerError>`). |
| 29 | +/// |
| 30 | +/// In both cases, the error type must implement `axum::response::IntoResponse`. |
| 31 | +/// |
| 32 | +/// ``` |
| 33 | +/// use axum::{response::IntoResponse, routing::post}; |
| 34 | +/// |
| 35 | +/// enum MyError{ |
| 36 | +/// Extractor(axum_sqlx_tx::Error), |
| 37 | +/// Layer(sqlx::Error), |
| 38 | +/// } |
| 39 | +/// |
| 40 | +/// impl From<axum_sqlx_tx::Error> for MyError { |
| 41 | +/// fn from(error: axum_sqlx_tx::Error) -> Self { |
| 42 | +/// Self::Extractor(error) |
| 43 | +/// } |
| 44 | +/// } |
| 45 | +/// |
| 46 | +/// impl From<sqlx::Error> for MyError { |
| 47 | +/// fn from(error: sqlx::Error) -> Self { |
| 48 | +/// Self::Layer(error) |
| 49 | +/// } |
| 50 | +/// } |
| 51 | +/// |
| 52 | +/// impl IntoResponse for MyError { |
| 53 | +/// fn into_response(self) -> axum::response::Response { |
| 54 | +/// // note that you would probably want to log the error as well |
| 55 | +/// (http::StatusCode::INTERNAL_SERVER_ERROR, "internal server error").into_response() |
| 56 | +/// } |
| 57 | +/// } |
| 58 | +/// |
| 59 | +/// // Override the `Tx` error type using the second generic type parameter |
| 60 | +/// type Tx = axum_sqlx_tx::Tx<sqlx::Sqlite, MyError>; |
| 61 | +/// |
| 62 | +/// # async fn foo() { |
| 63 | +/// let pool = sqlx::SqlitePool::connect("...").await.unwrap(); |
| 64 | +/// |
| 65 | +/// let (state, layer) = Tx::config(pool) |
| 66 | +/// // Override the `Layer` error type using the `Config` API |
| 67 | +/// .layer_error::<MyError>() |
| 68 | +/// .setup(); |
| 69 | +/// # let app = axum::Router::new() |
| 70 | +/// # .route("/", post(create_user)) |
| 71 | +/// # .layer(layer) |
| 72 | +/// # .with_state(state); |
| 73 | +/// # axum::Server::bind(todo!()).serve(app.into_make_service()); |
| 74 | +/// # } |
| 75 | +/// # async fn create_user(mut tx: Tx, /* ... */) { |
| 76 | +/// # /* ... */ |
| 77 | +/// # } |
| 78 | +/// ``` |
| 79 | +/// |
| 80 | +/// [`Tx`]: crate::Tx |
| 81 | +/// [`Layer`]: crate::Layer |
| 82 | +#[derive(Debug, thiserror::Error)] |
| 83 | +pub enum Error { |
| 84 | + /// Indicates that the [`Layer`](crate::Layer) middleware was not installed. |
| 85 | + #[error("required extension not registered; did you add the axum_sqlx_tx::Layer middleware?")] |
| 86 | + MissingExtension, |
| 87 | + |
| 88 | + /// Indicates that [`Tx`](crate::Tx) was extracted multiple times in a single |
| 89 | + /// handler/middleware. |
| 90 | + #[error("axum_sqlx_tx::Tx extractor used multiple times in the same handler/middleware")] |
| 91 | + OverlappingExtractors, |
| 92 | + |
| 93 | + /// A database error occurred when starting or committing the transaction. |
| 94 | + #[error(transparent)] |
| 95 | + Database { |
| 96 | + #[from] |
| 97 | + error: sqlx::Error, |
| 98 | + }, |
| 99 | +} |
| 100 | + |
| 101 | +impl axum_core::response::IntoResponse for Error { |
| 102 | + fn into_response(self) -> axum_core::response::Response { |
| 103 | + (http::StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response() |
| 104 | + } |
| 105 | +} |
0 commit comments