Debug School

rakesh kumar
rakesh kumar

Posted on

Build a High-Performance Vehicle Home Slider: Fast 9-Card Multi-Table Query with Partner/System Image Labels in Rust + React

======================env===============

.env

DATABASE_URL=mysql://user:password@127.0.0.1:3306/motoshare
BIND_ADDR=127.0.0.1:8080
SESSION_KEY=change-this-in-prod

DATABASE_URL=mysql://root:@127.0.0.1:3306/rust_crud
APP_BIND=127.0.0.1:8080
APP_NAME=Rust CRUD Dashboard
=============cargo.toml============
Cargo Setup

[dependencies]
actix-files = "0.6.8"
actix-multipart = "0.7.2"
actix-cors = "0.7.1"
actix-session = { version = "0.10.1", features = ["cookie-session"] }
actix-web = "4.11.0"
chrono = { version = "0.4.40", features = ["serde"] }
dotenvy = "0.15.7"
futures-util = "0.3.31"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
sqlx = { version = "0.8.3", features = ["runtime-tokio-rustls", "mysql", "chrono"] }
tera = "1.20.0"
tokio = { version = "1.44.1", features = ["fs", "io-util"] }
uuid = { version = "1.16.0", features = ["v4"] }

image = { version = "0.25.5", default-features = false, features = ["jpeg", "png", "webp", "gif"] }

Add Home DTO Models
C:\xampp\htdocs\rust_crud_app\src\models.rs

[derive(Debug, Clone, Serialize, Deserialize)]

pub struct HomeVehicleImage {
pub file: String,
pub src: String, // "partner" | "default"
}

[derive(Debug, Clone, Serialize, Deserialize)]

pub struct HomeVehicleCard {
pub id: u64,
pub vehical: String,
pub brand: String,
pub model: String,
pub city: String,
pub price: String,
pub dummy_data: String,
pub images: Vec,

}

DB Query (Fast + 9 rows + joins)
C:\xampp\htdocs\rust_crud_app\src\db\vehicle.rs
collections

use sqlx::MySqlPool;
use std::collections::HashSet;

use crate::models::{HomeVehicleCard, HomeVehicleImage, Vehicle, VehicleForm};

[derive(Debug, sqlx::FromRow)]

struct HomeVehicleRow {
id: u64,
vehical: Option,
brand: Option,
model: Option,
city: Option,
price: Option,
dummy_data: Option,
vechicle_image: Option,
}

fn push_images(
out: &mut Vec,
seen: &mut HashSet,
names: &[String],
src: &str,
max_count: usize,
) {
for name in names.iter().take(max_count) {
let clean = name.trim();
if clean.is_empty() || clean.eq_ignore_ascii_case("null") {
continue;
}
if !seen.insert(clean.to_string()) {
continue;
}
out.push(HomeVehicleImage {
file: clean.to_string(),
src: src.to_string(),
});
}
}

fn parse_images(raw: Option) -> Vec {
let txt = raw.unwrap_or_default();
let text = txt.trim();
if text.is_empty() {
return Vec::new();
}

let mut out = Vec::new();
let mut seen = HashSet::new();

if let Ok(v) = serde_json::from_str::<serde_json::Value>(text) {
    if let Some(obj) = v.as_object() {
        let partner = obj
            .get("partner")
            .and_then(|x| x.as_array())
            .map(|arr| {
                arr.iter()
                    .filter_map(|x| x.as_str().map(|s| s.to_string()))
                    .collect::<Vec<_>>()
            })
            .unwrap_or_default();
        let admin = obj
            .get("admin")
            .and_then(|x| x.as_array())
            .map(|arr| {
                arr.iter()
                    .filter_map(|x| x.as_str().map(|s| s.to_string()))
                    .collect::<Vec<_>>()
            })
            .unwrap_or_default();

        // 1 partner + 4 system images for home cards.
        push_images(&mut out, &mut seen, &partner, "partner", 1);
        push_images(&mut out, &mut seen, &admin, "default", 4);
        return out;
    }
}

// Legacy fallback: single filename stored directly.
let clean = text
    .trim_matches('"')
    .trim_matches('\'')
    .trim()
    .to_string();
if !clean.is_empty() && !clean.eq_ignore_ascii_case("null") {
    out.push(HomeVehicleImage {
        file: clean,
        src: "default".to_string(),
    });
}
out
Enter fullscreen mode Exit fullscreen mode

}

pub async fn list_home_vehicle_cards(
pool: &MySqlPool,
limit: i64,
) -> Result, sqlx::Error> {
let rows = sqlx::query_as::<_, HomeVehicleRow>(
"SELECT
av.id AS id,
CAST(COALESCE(bm.vehical, av.vechicle) AS CHAR) AS vehical,
CAST(COALESCE(av.brand, bm.brand) AS CHAR) AS brand,
CAST(COALESCE(av.model, bm.model) AS CHAR) AS model,
CAST(COALESCE(s.city, av.city) AS CHAR) AS city,
CAST(COALESCE(av.price, '') AS CHAR) AS price,
CAST(COALESCE(av.dummy_data, '0') AS CHAR) AS dummy_data,
CAST(COALESCE(av.vechicle_image, '') AS CHAR) AS vechicle_image
FROM addvechicles av
LEFT JOIN addvehical_byadmins bm ON bm.id = av.vehical_id
LEFT JOIN shops s ON s.id = av.shop_id
LEFT JOIN users u ON u.id = av.vender_ID
WHERE av.status = 'active'
ORDER BY av.id DESC
LIMIT ?",
)
.bind(limit)
.fetch_all(pool)
.await?;

Ok(rows
    .into_iter()
    .map(|r| HomeVehicleCard {
        id: r.id,
        vehical: r.vehical.unwrap_or_default(),
        brand: r.brand.unwrap_or_default(),
        model: r.model.unwrap_or_default(),
        city: r.city.unwrap_or_default(),
        price: r.price.unwrap_or_default(),
        dummy_data: r.dummy_data.unwrap_or_else(|| "0".to_string()),
        images: parse_images(r.vechicle_image),
    })
    .collect())
Enter fullscreen mode Exit fullscreen mode

}

============================
Export DB Function
C:\xampp\htdocs\rust_crud_app\src\db.rs

pub use vehicle::{
create_vehicle, delete_vehicle, get_vehicle_by_id, list_home_vehicle_cards, list_vehicles,
update_vehicle,

};

Add Handler
C:\xampp\htdocs\rust_crud_app\src\handlers\vehicle.rs
use crate::db::{
create_vehicle, delete_vehicle, get_vehicle_by_id, list_home_vehicle_cards, list_vehicles,
update_vehicle, AppState,
};
use crate::models::VehicleForm;
use super::common::{compress_uploaded_image, ensure_login_user_id, ApiResponse};

pub async fn list_home_vehicle_cards_handler(state: web::Data) -> Result {
let cards = list_home_vehicle_cards(&state.pool, 9)
.await
.map_err(error::ErrorInternalServerError)?;

let response = ApiResponse {
message: "Home vehicles fetched".to_string(),
data: cards,
};

Ok(HttpResponse::Ok().json(response))

Enter fullscreen mode Exit fullscreen mode




}

Export Handler
C:\xampp\htdocs\rust_crud_app\src\handlers.rs

pub use vehicle::{
create_vehicle_handler,
delete_vehicle_handler,
get_vehicle_handler,
list_home_vehicle_cards_handler,
list_vehicles_handler,
update_vehicle_handler,
upload_vehicle_documents_handler,
upload_vehicle_partner_image_handler,

};

Add Route
// src/handlers.rs

pub use vehicle::list_home_vehicle_cards_handler;

Frontend API Method
// frontend/src/api.js

listHomeVehicles: () => request("/home/vehicles"),

Load in App
// frontend/src/App.jsx
const [homeVehicles, setHomeVehicles] = useState([]);

const loadHomeVehicles = async () => {
try {
const result = await api.listHomeVehicles();
setHomeVehicles(Array.isArray(result.data) ? result.data.slice(0, 9) : []);
} catch {
const fallback = await api.listVehicles();
setHomeVehicles(Array.isArray(fallback.data) ? fallback.data.slice(0, 9) : []);
}
};

// call on boot
await loadHomeVehicles();

// pass to Home

Render Home Section
// frontend/src/pages/HomePage.jsx
import HomeAfterIntroSection from "../components/HomeAfterIntroSection";

export default function HomePage({ vehicles = [], ...props }) {
return (

{/* hero/search */}


);

}

Slider Component (auto-scroll + partner/system badge)
// frontend/src/components/HomeAfterIntroSection.jsx
// - parse v.images (new API)
// - fallback parse v.vechicle_image (legacy)
// - auto rotate every 3s

// - show badge: "uploaded by Partner" / "System photo"

DB Indexes for Performance
CREATE INDEX idx_addvechicles_home ON addvechicles (status, id);
CREATE INDEX idx_addvechicles_fk1 ON addvechicles (vehical_id);
CREATE INDEX idx_addvechicles_fk2 ON addvechicles (shop_id);
CREATE INDEX idx_addvechicles_fk3 ON addvechicles (vender_ID);
CREATE INDEX idx_addvehical_byadmins_id ON addvehical_byadmins (id);
CREATE INDEX idx_shops_id ON shops (id);
CREATE INDEX idx_users_id ON users (id);

============
cargo run

frontend

npm run dev

Top comments (0)