Server-Side Data Grid Transport & SQL Query Building
The ferrox-datagrid crate provides server-side data grid handling for Rust web frameworks. It translates frontend data grid request models (AG-Grid, TanStack Table) into optimized SeaORM / SQL queries with dynamic sorting, filtering, and pagination limits.
1. What It Is & Architectural Purposeβ
Web dashboards require fetching tabular datasets with multi-column filtering, flexible column sorting, dynamic text/range searching, and server-side pagination. Writing custom SQL query generators for every data table endpoint causes code duplication and SQL injection vulnerabilities.
ferrox-datagrid standardizes server-side data grid processing in Rust. It acts as an abstraction layer between frontend grid models and SeaORM / SQL query builders, guaranteeing type-safe parameter binding and zero SQL injection risks.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ferrox-datagrid Engine β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β’ Generic Grid Request Parser (AG-Grid / TanStack Table) β
β β’ Whitelisted Column Mapping Validator β
β β’ SeaORM Select<E> SQL Query Builder β
ββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββ
β Parameterized SQL Execution
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PostgreSQL / MySQL / SQLite Database β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. What It Does & Key Capabilitiesβ
- Universal Grid Request Model: Consumes standard JSON payload specifications containing
page,limit,sorts, andfilters. - SeaORM Integration: Converts filter models directly into SeaORM
Select<Entity>query conditions. - Relational Joined Column Resolution: Supports filtering and sorting across relational table joins.
- Strict Column Whitelisting: Matches incoming field names against explicit column whitelist schemas to block SQL injection.
3. How It Works Under the Hoodβ
Datagrid Query Generation Mechanicsβ
sequenceDiagram
autonumber
participant Client as AG-Grid Client
participant Router as Ferrox Transport Endpoint
participant Engine as ferrox-datagrid Engine
participant DB as SeaORM Select Query
Client->>Router: POST /api/users/grid (IServerSideGetRowsRequest)
Router->>Engine: DatagridEngine::apply(select_query, request, entity_map)
Engine->>Engine: Validate Field Names against Whitelist Schema
Engine->>Engine: Append Parameterized WHERE Conditions
Engine->>Engine: Apply ORDER BY & LIMIT / OFFSET Clause
Engine->>DB: Execute get_many_and_count()
DB-->>Engine: Return (Vec<Model>, total_count)
Engine-->>Router: DatagridResponse { data, total, page }
Router-->>Client: 200 OK JSON Response
4. Why It Was Designed This Wayβ
| Feature | Raw SQL String Parsing | ferrox-datagrid Engine |
|---|---|---|
| Security | Un-sanitized field parameters allow SQL injection. | 100% Parameterized queries with column whitelist enforcement. |
| Developer Speed | Writing custom search logic takes days per entity table. | Single service function processes any grid endpoint. |
| Performance | Inefficient table scans from missing index joins. | Generates optimized SQL queries tailored to database indexes. |
5. Practical Usage Guide & Extended Code Examplesβ
5.1 Service-Layer Grid Query Processingβ
use ferrox_datagrid::{DatagridEngine, DatagridRequest, ColumnMap};
use sea_orm::*;
pub async fn fetch_users_grid(
db: &DatabaseConnection,
grid_request: DatagridRequest,
) -> Result<DatagridResult<user::Model>, DbErr> {
let mut query = user::Entity::find();
// Map frontend grid field names to SeaORM entity columns
let column_map = ColumnMap::new()
.insert("id", user::Column::Id)
.insert("email", user::Column::Email)
.insert("status", user::Column::Status)
.insert("createdAt", user::Column::CreatedAt);
let datagrid = DatagridEngine::new(query, grid_request, column_map);
let (data, total) = datagrid.execute(db).await?;
Ok(DatagridResult {
data,
total,
page: grid_request.page.unwrap_or(1),
limit: grid_request.limit.unwrap_or(20),
})
}
6. Anti-Patterns: How NOT to Use Itβ
[!CAUTION] Anti-Pattern 1: Un-whitelisted Dynamic Column Expressions Never pass client-provided column strings directly to
.order_by_asc()without validating them against aColumnMapwhitelist. Un-sanitized inputs expose database schema details.
7. Pro-Tips & Best Practicesβ
[!TIP] Pro-Tip 1: Default Fallback Sorting Always provide a fallback default sort column (e.g.,
user::Column::Id DESC) to ensure deterministic pagination ordering when client sort models are empty.