Backend Dependencies - Mardens-Inc/Pricing-App GitHub Wiki

This document explains every backend dependency, why it was chosen, and how it's used in the Pricing App.

Table of Contents


Overview

The backend uses 30+ dependencies chosen for performance, type safety, and developer experience. All dependencies are specified in Cargo.toml with exact or minimum versions.

Total Dependency Count: 30 direct dependencies Rust Edition: 2024 Current Version: 1.5.4


Core Framework

actix-web (4.9.0)

actix-web = "4.9.0"

Purpose: Web framework providing HTTP server, routing, middleware, and extractors.

Why chosen:

  • Top-tier performance (one of fastest web frameworks)
  • Mature ecosystem with excellent middleware support
  • Type-safe extractors for request data
  • Built-in async/await support
  • Active maintenance

Key features used:

  • web::scope() for route grouping
  • web::Data<T> for shared state (database pool)
  • web::Json<T> for automatic JSON serialization
  • HttpRequest for accessing request metadata
  • Middleware support for authentication

Example:

#[post("/api/inventory/{id}")]
pub async fn create_record(
    pool: web::Data<MySqlPool>,
    id: web::Path<String>,
    data: web::Json<RecordRequest>,
) -> Result<impl Responder> {
    // Handler implementation
}

actix-files (0.6.6)

actix-files = "0.6.6"

Purpose: Static file serving for production builds.

Why chosen:

  • Official Actix ecosystem package
  • Efficient static file serving
  • ETag support for caching
  • Content-type detection

Usage: Serves frontend assets from target/wwwroot/ in production.

actix_files::Files::new("/", "./target/wwwroot/")
    .index_file("index.html")

actix-web-lab (0.24.1)

actix-web-lab = "0.24.1"

Purpose: Experimental Actix features, primarily Server-Sent Events (SSE).

Why chosen:

  • Official SSE support for real-time updates
  • Clean API for streaming responses
  • Better than polling for inventory updates

Usage: Real-time inventory change notifications.

use actix_web_lab::sse;

pub async fn inventory_updates(
    id: web::Path<String>
) -> Result<impl Responder> {
    let (tx, rx) = channel::unbounded();

    Ok(sse::Sse::from_stream(rx))
}

actix-authentication-middleware (0.1.9)

actix-authentication-middleware = "0.1.9"

Purpose: Custom authentication middleware for protected routes.

Why chosen:

  • Project-specific authentication requirements
  • JWT token validation
  • User context injection

Usage: Wraps protected endpoints.

web::scope("/api/inventory")
    .wrap(AuthenticationMiddleware::new())
    .route("/{id}", web::get().to(get_inventory))

Database & Persistence

sqlx (0.8.2)

sqlx = { version = "0.8.2", features = ["mysql", "derive", "chrono", "runtime-tokio", "time", "runtime-tokio-rustls"] }

Purpose: Async MySQL driver with compile-time query verification.

Why chosen over ORMs (Diesel/SeaORM):

  • Compile-time SQL checking prevents runtime errors
  • Async-first design (essential for Actix)
  • Direct SQL control for complex queries
  • No runtime ORM overhead
  • Dynamic table names are straightforward (needed for {location_id} tables)

Features explained:

  • mysql - MySQL protocol support
  • derive - FromRow derive macro
  • chrono - Date/time support
  • runtime-tokio - Tokio async runtime integration
  • runtime-tokio-rustls - TLS support with rustls
  • time - Time type support

Example:

#[derive(FromRow)]
struct Location {
    id: u64,
    name: String,
    location: String,
}

let locations: Vec<Location> = sqlx::query_as(
    "SELECT id, name, location FROM locations WHERE active = ?"
)
.bind(true)
.fetch_all(&pool)
.await?;

database-common-lib (custom)

database-common-lib = { path = "../database-common-lib" }

Purpose: Shared database utilities across Mardens projects.

Why chosen:

  • Code reuse across multiple applications
  • Consistent connection handling
  • Standardized error responses

Provides:

  • Database connection creation from dev-server.json
  • HTTP error types (http_error::Result)
  • Common query helpers

Serialization & Data Formats

serde (1.0.208) + serde_derive (1.0.210)

serde = { version = "1.0.208", features = ["derive"] }
serde_derive = "1.0.210"

Purpose: Serialization framework for Rust.

Why chosen:

  • Industry standard for Rust serialization
  • Zero-cost abstractions
  • Automatic JSON/form data conversion
  • Excellent error messages

Example:

#[derive(Serialize, Deserialize)]
pub struct LocationRequest {
    pub name: String,
    pub location: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub icon: Option<String>,
}

serde_json (1.0.125)

serde_json = "1.0.125"

Purpose: JSON serialization/deserialization.

Usage:

  • API request/response bodies
  • Storing complex data in TEXT columns
  • History diff storage
let json = serde_json::to_string(&data)?;
let value: Value = serde_json::from_str(&json)?;

serde_urlencoded (0.7.1)

serde_urlencoded = "0.7.1"

Purpose: URL-encoded form data parsing.

Usage: Parsing form data from spreadsheet upload flows.

serde_hash (0.1.3)

serde_hash = "0.1.3"

Purpose: ID hashing/encoding with hashids algorithm.

Why chosen:

  • Security: Prevents ID enumeration attacks
  • Privacy: Obscures database structure
  • Reversible: Can decode back to integer

Configuration:

  • Salt: "pricing-app"
  • Min length: 16 characters
  • Alphabet: URL-safe characters

Example:

use serde_hash::hashids::{encode_single, decode_single};

let hashed = encode_single(123); // "x7J8kLm9N2pQr4Tv"
let id = decode_single("x7J8kLm9N2pQr4Tv")?; // 123

File Processing

csv (1.3.1)

csv = "1.3.1"

Purpose: CSV file parsing for inventory imports.

Why chosen:

  • Fast and memory-efficient
  • Handles various CSV formats
  • Good error messages

Usage: Parse uploaded CSV files for spreadsheet import.

let mut reader = csv::Reader::from_path(&file_path)?;
for result in reader.deserialize() {
    let record: Record = result?;
    // Process record
}

calamine (0.26.1)

calamine = { version = "0.26.1", features = ["chrono", "dates"] }

Purpose: Excel file (.xlsx, .xls) parsing.

Why chosen:

  • Pure Rust implementation (no external dependencies)
  • Supports both old (.xls) and new (.xlsx) formats
  • Date/time handling with chrono integration
  • Memory efficient for large files

Features:

  • chrono - Date conversions
  • dates - Excel date format support

Usage: Read Excel files for inventory imports.

use calamine::{open_workbook, Reader, Xlsx};

let mut workbook: Xlsx<_> = open_workbook(&file_path)?;
if let Some(Ok(range)) = workbook.worksheet_range("Sheet1") {
    for row in range.rows() {
        // Process row
    }
}

zip (2.4.2)

zip = "2.4.2"

Purpose: Zip archive handling (Excel .xlsx files are zip archives).

Why chosen:

  • Required by calamine for .xlsx support
  • Can extract specific files from archive
  • Memory-efficient streaming

Usage: Indirect (used by calamine), but also for future archive features.

sanitize-filename (0.6.0)

sanitize-filename = "0.6.0"

Purpose: Clean user-provided filenames for safe filesystem storage.

Why chosen:

  • Prevents directory traversal attacks
  • Removes unsafe characters
  • Cross-platform compatible

Example:

let safe_name = sanitize_filename::sanitize(&user_provided_name);
let path = format!("uploads/{}", safe_name);

API Documentation

utoipa (5.3.1)

utoipa = { version = "5.3.1", features = ["actix_extras"] }

Purpose: OpenAPI 3.0 specification generation from code.

Why chosen over hand-written docs:

  • Documentation always in sync with code
  • Compile-time validation of documentation
  • Automatic schema generation
  • Type-safe documentation

Features:

  • actix_extras - Integration with Actix-web extractors

Usage: Every endpoint has a #[utoipa::path] macro.

#[utoipa::path(
    get,
    path = "/api/list/{id}",
    params(
        ("id" = String, Path, description = "Location ID")
    ),
    responses(
        (status = 200, description = "Success", body = Location),
        (status = 404, description = "Not found")
    ),
    tag = "Locations"
)]
pub async fn get_location(...) { }

utoipa-swagger-ui (9.0.0)

utoipa-swagger-ui = { version = "9.0.0", features = ["actix-web"] }

Purpose: Swagger UI for interactive API documentation.

Why chosen:

  • Industry-standard API exploration tool
  • Try-it-out functionality
  • Auto-generated from OpenAPI spec

Access: http://localhost:1421/docs/swagger/

utoipa-rapidoc (6.0.0)

utoipa-rapidoc = { version = "6.0.0", features = ["actix-web"] }

Purpose: Alternative API documentation UI.

Why chosen:

  • Modern, fast interface
  • Better for complex APIs
  • Additional visualization options

Access: http://localhost:1421/docs/rapidoc/


Async Runtime

tokio (1.40.0)

tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "macros", "sync", "time"] }

Purpose: Async runtime for all I/O operations.

Why chosen:

  • Industry standard for async Rust
  • Required by Actix and SQLx
  • Excellent performance
  • Rich ecosystem

Features:

  • rt - Runtime basics
  • rt-multi-thread - Multi-threaded work-stealing scheduler
  • macros - #[tokio::main] and #[tokio::test]
  • sync - Async primitives (channels, mutexes)
  • time - Async timers

Example:

#[tokio::main]
async fn main() -> Result<()> {
    // Async code here
}

// Spawning background tasks
tokio::spawn(async move {
    // Background work
});

tokio-stream (0.1.17)

tokio-stream = { version = "0.1.17", features = ["sync", "tokio-util"] }

Purpose: Stream utilities for async data flows.

Features:

  • sync - Channel-based streams
  • tokio-util - Additional utilities

Usage: SSE stream handling for real-time updates.

futures (0.3.31)

futures = "0.3.31"

Purpose: Future combinators and utilities.

Why needed:

  • Additional async utilities not in tokio
  • Future joins and selects
  • Stream transformations

Example:

use futures::future::join_all;

let results = join_all(vec![
    async_operation_1(),
    async_operation_2(),
]).await;

Utilities

chrono (0.4.38)

chrono = { version = "0.4.38", features = ["serde"] }

Purpose: Date and time handling.

Features:

  • serde - Serialization support

Usage:

  • Timestamp fields in database
  • Date parsing from Excel files
  • History tracking
use chrono::{DateTime, Utc, NaiveDate};

let now: DateTime<Utc> = Utc::now();
let date = NaiveDate::from_ymd_opt(2025, 1, 15)?;

regex (1.11.1)

regex = "1.11.1"

Purpose: Regular expression matching.

Usage:

  • Validation of input formats
  • Column name cleaning
  • Text pattern matching
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$")?;
if re.is_match(&input) {
    // Valid date format
}

uuid (1.13.1)

uuid = { version = "1.13.1", features = ["fast-rng", "serde", "v4"] }

Purpose: UUID generation for file uploads.

Features:

  • fast-rng - Fast random number generation
  • serde - Serialization
  • v4 - Random UUIDs

Usage: Temporary file identifiers.

use uuid::Uuid;

let id = Uuid::new_v4();
let filename = format!("uploads/{}.xlsx", id);

anyhow (1.0.97)

anyhow = "1.0.97"

Purpose: Ergonomic error handling.

Why chosen:

  • Simplified error propagation with ?
  • Context can be added to errors
  • Good for application-level code

Example:

use anyhow::{Result, Context};

fn process_file(path: &str) -> Result<()> {
    let content = std::fs::read_to_string(path)
        .context("Failed to read file")?;
    // Process content
    Ok(())
}

log (0.4.22)

log = "0.4.22"

Purpose: Logging facade.

Usage: All logging calls throughout the application.

log::debug!("Processing request: {}", id);
log::info!("Created new location");
log::warn!("Slow query: {}ms", duration);
log::error!("Database error: {}", err);

pretty_env_logger (0.5.0)

pretty_env_logger = "0.5.0"

Purpose: Pretty-printed logging output.

Why chosen:

  • Human-readable log format
  • Color-coded log levels
  • Environment variable configuration

Setup:

pretty_env_logger::init();

Environment:

RUST_LOG=debug cargo run

stopwatch (0.0.7)

stopwatch = "0.0.7"

Purpose: Performance timing for slow query detection.

Usage:

let sw = Stopwatch::start_new();
// ... operation ...
log::warn!("Slow operation: {}ms", sw.elapsed_ms());

rayon (1.10.0)

rayon = "1.10.0"

Purpose: Data parallelism for CPU-bound tasks.

Why chosen:

  • Easy parallelization of iterators
  • Work-stealing scheduler
  • Good for batch processing

Usage: Parallel processing of large spreadsheets.

use rayon::prelude::*;

let results: Vec<_> = items.par_iter()
    .map(|item| process(item))
    .collect();

HTTP & Network

reqwest (0.12.9)

reqwest = { version = "0.12.9", features = ["json", "rustls-tls"] }

Purpose: HTTP client for external API calls.

Features:

  • json - Automatic JSON handling
  • rustls-tls - Pure Rust TLS (no OpenSSL dependency)

Usage: Making requests to external services.

let response = reqwest::get("https://api.example.com/data")
    .await?
    .json::<DataType>()
    .await?;

ftp (3.0.1)

ftp = "3.0.1"

Purpose: FTP client for POS system integration.

Why needed:

  • Legacy POS systems use FTP
  • Upload pricing data to retail locations

Usage: See src-actix/pos_system/ftp_data.rs

let mut ftp = FtpStream::connect("ftp.example.com:21")?;
ftp.login("user", "pass")?;
ftp.put("remote.txt", &mut file)?;

Development Tools

vite-actix (>=0.2.6)

vite-actix = ">=0.2.6"

Purpose: Integration between Vite dev server and Actix backend.

Why chosen:

  • Seamless development experience
  • Automatic proxy setup
  • Hot module replacement for frontend
  • Single npm run dev command

Usage: Spawns Vite dev server from Rust in debug mode.

include_dir (0.7.4)

include_dir = "0.7.4"

Purpose: Embed static files in binary at compile time.

Why chosen:

  • Single binary deployment
  • No need to copy frontend files
  • Production mode serves embedded assets

Usage:

static ASSETS: Dir = include_dir!("$CARGO_MANIFEST_DIR/target/wwwroot");

urlencoding (2.1.2) [dev-dependency]

[dev-dependencies]
urlencoding = "2.1.2"

Purpose: URL encoding for tests.

Usage: Encoding test data in integration tests.


Dependency Management

Version Strategy

  • Exact versions for critical dependencies (actix-web, sqlx)
  • Minimum versions (>=) for flexible dependencies (vite-actix)
  • Caret ranges (^) for stable libraries (defaults)

Updating Dependencies

# Check for updates
cargo outdated

# Update within version constraints
cargo update

# Update to latest versions (edit Cargo.toml)
# Then run:
cargo update

Security Auditing

# Install audit tool
cargo install cargo-audit

# Run security audit
cargo audit

Build Times

To reduce build times during development:

# Use mold linker (Linux)
sudo apt install mold
RUSTFLAGS="-C link-arg=-fuse-ld=mold" cargo build

# Use lld linker (cross-platform)
RUSTFLAGS="-C link-arg=-fuse-ld=lld" cargo build

Summary

By Category

Category Count Key Libraries
Web Framework 4 actix-web, actix-files, actix-web-lab
Database 2 sqlx, database-common-lib
Serialization 5 serde, serde_json, serde_hash
File Processing 4 csv, calamine, zip, sanitize-filename
API Docs 3 utoipa, utoipa-swagger-ui, utoipa-rapidoc
Async Runtime 3 tokio, tokio-stream, futures
Utilities 7 chrono, regex, uuid, anyhow, log
Network 2 reqwest, ftp
Dev Tools 2 vite-actix, include_dir

Total: 32 dependencies

Unique Choices

  1. SQLx over ORM - Direct SQL control for dynamic tables
  2. serde_hash - Custom ID obfuscation
  3. calamine - Pure Rust Excel parsing
  4. utoipa - Code-first API documentation
  5. actix-web-lab - SSE for real-time updates
  6. vite-actix - Seamless dev integration

Next Steps


Last Updated: 2025-11-04

⚠️ **GitHub.com Fallback** ⚠️