I'm a beginner working on a Rust project where I'm trying to integrate Diesel with Actix for building a RESTful API. Despite searching through forums and using AI tools for assistance, I'm struggling to come up with a clean and understandable project structure that not only adheres to best practices but also helps me resolve the errors I'm encountering.
Here’s a brief overview of my current situation:
- Project Structure: I've organized my project into different modules like
models
,handlers
,routes
, etc. However, I'm not sure if this is the best approach for a beginner. - Errors: I’m facing various issues related to Diesel queries, handling errors in Actix, and managing database connections. Specifically, I’m having trouble with using Diesel's asynchronous capabilities within Actix, especially when it comes to error handling and making the code more modular and readable.
I would greatly appreciate any advice or examples on how to structure such a project effectively. I’m looking for a solution that is beginner-friendly but still adheres to Rust’s best practices for building web applications. If anyone could provide a sample project structure or point me to resources that might help, I would be extremely grateful.
Here is my current project structure and code:
src/
main.rs
routes.rs
handlers/
mod.rs
user.rs
models/
mod.rs
user.rs
schema.rs
lib.rs
.env
\ main.rs
use actix_web::{App, HttpServer, web};
use diesel::r2d2::{self, ConnectionManager};
use diesel::MysqlConnection;
use dotenv::dotenv;
use std::env;
use anyhow::{Context, Result};
use poat_db::DbPool;
use poat_db::routes;
use actix_web::web::Data;
#[actix_web::main]
async fn main() -> Result<()> {
log4rs::init_file("src/config/log4rs.yaml", Default::default())
.context("Error ao carregar as configurações do Log4rs")?;
dotenv().context("Falha ao carregar o arquivo .env")?;
let database_url = env::var("DATABASE_URL").context("DATABASE_URL must be set")?;
let manager = ConnectionManager::<MysqlConnection>::new(database_url);
let pool = r2d2::Pool::builder().build(manager).expect("Failed to create pool.");
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(pool.clone()))
.configure(routes::configure)
})
.bind("127.0.0.1:8080")
.context("Failed to bind port")?
.run()
.await
.context("Failed to starting server")
}
\ routes.rs
use actix_web::web;
use crate::handlers::users::{get_all_users, create_user};
pub fn configure(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/users")
.route("/", web::get().to(get_all_users))
.route("/", web::post().to(create_user))
);
}
\ model/user.rs
use diesel::{prelude::*, MysqlConnection, Queryable, Insertable};
use serde::{Deserialize, Serialize};
use chrono::NaiveDateTime;
use crate::schema::users;
#[derive(Queryable, Serialize, Deserialize)]
pub struct User {
pub id: i32,
pub name: String,
pub email: String,
pub password: String,
pub created_at: Option<NaiveDateTime>,
}
#[derive(Insertable, Deserialize, Serialize)]
#[diesel(table_name = users)] // Corrigido o caminho para o schema
pub struct UserDTO {
pub name: String,
pub email: String,
pub password: String,
}
impl User {
pub fn get_all(conn: &mut MysqlConnection) -> QueryResult<Vec<Self>> {
users::table.load::<User>(conn)
}
pub fn create(conn: &MysqlConnection, new_user: UserDTO) -> QueryResult<Self> {
diesel::insert_into(users::table)
.values(&new_user)
.get_result(conn)
}
}
\ handlers/user.rs
use actix_web::{web, HttpResponse, Error};
use crate::models::users::{User, UserDTO};
use crate::DbPool;
use crate::schema::users::dsl::*;
use diesel::prelude::*;
pub async fn get_all_users(pool: web::Data<DbPool>) -> Result<HttpResponse, Error> {
let mut conn = pool.get().map_err(|_| actix_web::error::ErrorInternalServerError("Failed to get DB connection"))?;
let query_result = users
.load::<User>(&mut conn)
.map_err(|_| actix_web::error::ErrorInternalServerError("Failed to retrieve users"))?;
Ok(HttpResponse::Ok().json(query_result))
}
pub async fn create_user(pool: web::Data<DbPool>, new_user: web::Json<UserDTO>) -> Result<HttpResponse, Error> {
let mut conn = pool.get().map_err(|_| actix_web::error::ErrorInternalServerError("Failed to get DB connection"))?;
let user = web::block(move || User::create(&mut conn, new_user.into_inner())).await.map_err(|_| actix_web::error::ErrorInternalServerError("Failed to insert users"))?;
Ok(HttpResponse::Ok().json(user))
}
\ lib.rs
use diesel::r2d2::{self, ConnectionManager};
use diesel::MysqlConnection;
pub type DbPool = r2d2::Pool<ConnectionManager<MysqlConnection>>;
pub mod routes;
pub mod handlers;
pub mod models;
pub mod schema;
Thank you so much for your time and help!