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.
- Overview
- Core Framework
- Database & Persistence
- Serialization & Data Formats
- File Processing
- API Documentation
- Async Runtime
- Utilities
- HTTP & Network
- Development Tools
- Dependency Management
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
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 -
HttpRequestfor 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"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"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"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))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-FromRowderive 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 = { 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
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"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"Purpose: URL-encoded form data parsing.
Usage: Parsing form data from spreadsheet upload flows.
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")?; // 123csv = "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 = { 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"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"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);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 = { 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 = { 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/
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 = { 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"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;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"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 = { 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"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"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"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 runstopwatch = "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"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();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"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)?;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 devcommand
Usage: Spawns Vite dev server from Rust in debug mode.
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");[dev-dependencies]
urlencoding = "2.1.2"Purpose: URL encoding for tests.
Usage: Encoding test data in integration tests.
- Exact versions for critical dependencies (actix-web, sqlx)
- Minimum versions (>=) for flexible dependencies (vite-actix)
- Caret ranges (^) for stable libraries (defaults)
# Check for updates
cargo outdated
# Update within version constraints
cargo update
# Update to latest versions (edit Cargo.toml)
# Then run:
cargo update# Install audit tool
cargo install cargo-audit
# Run security audit
cargo auditTo 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| 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
- SQLx over ORM - Direct SQL control for dynamic tables
- serde_hash - Custom ID obfuscation
- calamine - Pure Rust Excel parsing
- utoipa - Code-first API documentation
- actix-web-lab - SSE for real-time updates
- vite-actix - Seamless dev integration
- Backend Database - See how SQLx is used
- Backend API Design - Understand utoipa documentation
- Backend Testing - Testing with Actix test utilities
Last Updated: 2025-11-04