From 32c9ab3c5682a2f9599f8e5ccb0b45b5e71f59c1 Mon Sep 17 00:00:00 2001
From: FlakM <maciej.jan.flak@gmail.com>
Date: Tue, 25 Jan 2022 16:20:00 +0100
Subject: [PATCH] Add sqlx example with migrations (#722)

* Add sqlx example with migrations

Simple use case for sqlx based on tokio postgres example.
Sqlite database is created on execution in ./target directory
and migrations are then run against it.

* sqlx example uses postgres instead of sqlite3

Also removed migrations and database creation code.
---
 examples/sqlx-postgres/Cargo.toml  |  14 ++++
 examples/sqlx-postgres/src/main.rs | 112 +++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+)
 create mode 100644 examples/sqlx-postgres/Cargo.toml
 create mode 100644 examples/sqlx-postgres/src/main.rs

diff --git a/examples/sqlx-postgres/Cargo.toml b/examples/sqlx-postgres/Cargo.toml
new file mode 100644
index 00000000..32bfb916
--- /dev/null
+++ b/examples/sqlx-postgres/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "example-sqlx-postgres"
+version = "0.1.0"
+edition = "2018"
+publish = false
+
+[dependencies]
+axum = { path = "../../axum" }
+tokio = { version = "1.0", features = ["full"] }
+tracing = "0.1"
+tracing-subscriber = { version="0.3", features = ["env-filter"] }
+
+
+sqlx = { version = "0.5.10", features = ["runtime-tokio-rustls", "any", "postgres"] }
diff --git a/examples/sqlx-postgres/src/main.rs b/examples/sqlx-postgres/src/main.rs
new file mode 100644
index 00000000..655f9c74
--- /dev/null
+++ b/examples/sqlx-postgres/src/main.rs
@@ -0,0 +1,112 @@
+//! Example of application using https://github.com/launchbadge/sqlx
+//!
+//! Run with
+//!
+//! ```not_rust
+//! cargo run -p example-sqlx-postgres
+//! ```
+//!
+//! Test with curl:
+//!
+//! ```not_rust
+//! curl 127.0.0.1:3000
+//! curl -X POST 127.0.0.1:3000
+//! ```
+
+use axum::{
+    async_trait,
+    extract::{Extension, FromRequest, RequestParts},
+    http::StatusCode,
+    routing::get,
+    AddExtensionLayer, Router,
+};
+use sqlx::postgres::{PgPool, PgPoolOptions};
+
+use std::{net::SocketAddr, time::Duration};
+
+#[tokio::main]
+async fn main() {
+    // Set the RUST_LOG, if it hasn't been explicitly defined
+    if std::env::var_os("RUST_LOG").is_none() {
+        std::env::set_var("RUST_LOG", "example_tokio_postgres=debug")
+    }
+    tracing_subscriber::fmt::init();
+
+    let db_connection_str = std::env::var("DATABASE_URL")
+        .unwrap_or_else(|_| "postgres://postgres:password@localhost".to_string());
+
+    // setup connection pool
+    let pool = PgPoolOptions::new()
+        .max_connections(5)
+        .connect_timeout(Duration::from_secs(3))
+        .connect(&db_connection_str)
+        .await
+        .expect("can connect to database");
+
+    // build our application with some routes
+    let app = Router::new()
+        .route(
+            "/",
+            get(using_connection_pool_extractor).post(using_connection_extractor),
+        )
+        .layer(AddExtensionLayer::new(pool));
+
+    // run it with hyper
+    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
+    tracing::debug!("listening on {}", addr);
+    axum::Server::bind(&addr)
+        .serve(app.into_make_service())
+        .await
+        .unwrap();
+}
+
+// we can exact the connection pool with `Extension`
+async fn using_connection_pool_extractor(
+    Extension(pool): Extension<PgPool>,
+) -> Result<String, (StatusCode, String)> {
+    sqlx::query_scalar("select 'hello world from pg'")
+        .fetch_one(&pool)
+        .await
+        .map_err(internal_error)
+}
+
+// we can also write a custom extractor that grabs a connection from the pool
+// which setup is appropriate depends on your application
+struct DatabaseConnection(sqlx::pool::PoolConnection<sqlx::Postgres>);
+
+#[async_trait]
+impl<B> FromRequest<B> for DatabaseConnection
+where
+    B: Send,
+{
+    type Rejection = (StatusCode, String);
+
+    async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
+        let Extension(pool) = Extension::<PgPool>::from_request(req)
+            .await
+            .map_err(internal_error)?;
+
+        let conn = pool.acquire().await.map_err(internal_error)?;
+
+        Ok(Self(conn))
+    }
+}
+
+async fn using_connection_extractor(
+    DatabaseConnection(conn): DatabaseConnection,
+) -> Result<String, (StatusCode, String)> {
+    let mut conn = conn;
+    sqlx::query_scalar("select 'hello world from pg'")
+        .fetch_one(&mut conn)
+        .await
+        .map_err(internal_error)
+}
+
+/// Utility function for mapping any error into a `500 Internal Server Error`
+/// response.
+fn internal_error<E>(err: E) -> (StatusCode, String)
+where
+    E: std::error::Error,
+{
+    (StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
+}