mod models { use serde::{Deserialize, Serialize}; use tokio_pg_mapper_derive::PostgresMapper; #[derive(Deserialize, PostgresMapper, Serialize)] #[pg_mapper(table = "users")] // singular 'user' is a keyword.. pub struct User { pub email: String, pub first_name: String, pub last_name: String, pub username: String, } } mod errors { use actix_web::{HttpResponse, ResponseError}; use deadpool_postgres::PoolError; use derive_more::{Display, From}; use tokio_pg_mapper::Error as PGMError; use tokio_postgres::error::Error as PGError; #[derive(Display, From, Debug)] pub enum MyError { NotFound, PGError(PGError), PGMError(PGMError), PoolError(PoolError), } impl std::error::Error for MyError {} impl ResponseError for MyError { fn error_response(&self) -> HttpResponse { match *self { MyError::NotFound => HttpResponse::NotFound().finish(), _ => HttpResponse::InternalServerError().finish(), } } } } mod db { use crate::{errors::MyError, models::User}; use deadpool_postgres::Client; use tokio_pg_mapper::FromTokioPostgresRow; pub async fn add_user(client: &Client, user_info: User) -> Result { let _stmt = include_str!("../sql/add_user.sql"); let _stmt = _stmt.replace("$table_fields", &User::sql_table_fields()); let stmt = client.prepare(&_stmt).await.unwrap(); client .query( &stmt, &[ &user_info.email, &user_info.first_name, &user_info.last_name, &user_info.username, ], ) .await? .iter() .map(|row| User::from_row_ref(row).unwrap()) .collect::>() .pop() .ok_or(MyError::NotFound) // more applicable for SELECTs } } mod handlers { use crate::{db, errors::MyError, models::User}; use actix_web::{web, Error, HttpResponse}; use deadpool_postgres::{Client, Pool}; pub async fn add_user( user: web::Json, db_pool: web::Data, ) -> Result { let user_info: User = user.into_inner(); let client: Client = db_pool.get().await.map_err(|err| MyError::PoolError(err))?; let new_user = db::add_user(&client, user_info).await?; Ok(HttpResponse::Ok().json(new_user)) } } use actix_web::{web, App, HttpServer}; use deadpool_postgres::{Manager, Pool}; use handlers::add_user; use tokio_postgres::{Config, NoTls}; #[actix_rt::main] async fn main() -> std::io::Result<()> { const SERVER_ADDR: &str = "127.0.0.1:8080"; let pg_config = "postgres://test_user:testing@127.0.0.1:5432/testing_db" .parse::() .unwrap(); let pool = Pool::new( Manager::new(pg_config, NoTls), 16, // # of connections in pool ); let server = HttpServer::new(move || { App::new() .data(pool.clone()) .service(web::resource("/users").route(web::post().to(add_user))) }) .bind(SERVER_ADDR)? .run(); println!("Server running at http://{}/", SERVER_ADDR); server.await }