A comprehensive Rust web API foundation library providing reusable patterns and utilities for building consistent, maintainable web APIs.
DateRangeQuery
- Simple date-based filtering (YYYY-MM-DD format)DateTimeRangeQuery
- Precise datetime filtering with timezone support (RFC 3339)PaginationQuery
- Page-based pagination with metadataSortQuery
- Flexible multi-field sorting with SQL generation
ApiResponse<T>
- Standardized JSON response formatPaginatedData<T>
- Paginated response wrapper with metadata- Response macros -
data!()
,empty!()
for quick responses
AppError
- Comprehensive error types for web applications- Unified HTTP 200 responses - All responses return HTTP 200 with business codes in JSON
- Business code mapping - Errors map to semantic business codes (SUCCESS, VALIDATION_ERROR, etc.)
- Validation integration - Seamless validator crate integration
ValidatedJson<T>
- JSON extractor with automatic validation- Custom validators - Password strength, phone numbers, email, etc.
- Query validation - Built-in validation for all query parameter types
- Middleware helpers - CORS, request ID, tracing integration
- OpenAPI support - Full utoipa integration with examples
- Feature flags - Optional dependencies (axum, sqlx, jwt)
Add this to your Cargo.toml
:
[dependencies]
avinapi = "0.1.0"
# Optional features
avinapi = { version = "0.1.0", features = ["axum", "sqlx", "jwt"] }
axum
(default) - Axum web framework integrationsqlx
(default) - Database integration utilitiesjwt
(default) - JWT authentication helpers
use avinapi::prelude::*;
use axum::{Router, extract::Query, routing::get};
#[derive(serde::Serialize)]
struct User {
id: u32,
name: String,
email: String,
}
// Simple pagination endpoint
async fn list_users(
Query(pagination): Query<PaginationQuery>,
) -> JsonResult<PaginatedData<User>> {
let users = get_users_from_db(
pagination.get_limit(),
pagination.get_offset(),
).await?;
let total_count = count_users().await?;
let response = PaginatedData::new(users, &pagination, total_count);
data!(response)
}
// Advanced filtering with multiple query parameters
async fn list_orders(
Query(pagination): Query<PaginationQuery>,
Query(date_range): Query<DateTimeRangeQuery>,
Query(sort): Query<SortQuery>,
) -> JsonResult<PaginatedData<Order>> {
// Validate datetime range (max 30 days)
date_range.validate_max_duration(30)?;
// Parse to UTC for database queries
let start_utc = date_range.parse_start_utc()?;
let end_utc = date_range.parse_end_utc()?;
// Generate SQL ORDER BY clause
let order_clause = sort.to_sql_with_prefix("orders");
let orders = query_orders(start_utc, end_utc, order_clause, pagination).await?;
data!(orders)
}
fn main() {
let app = Router::new()
.route("/users", get(list_users))
.route("/orders", get(list_orders));
// ... run server
}
// URL: /reports?start_date=2024-01-01&end_date=2024-12-31
async fn reports(Query(date_range): Query<DateRangeQuery>) -> JsonResult<Vec<Report>> {
let reports = Report::find_by_date_range(
date_range.start_date,
date_range.end_date
).await?;
data!(reports)
}
// URL: /events?start_datetime=2024-12-19T10:30:00+08:00&end_datetime=2024-12-19T18:00:00+08:00
async fn events(Query(datetime_range): Query<DateTimeRangeQuery>) -> JsonResult<Vec<Event>> {
// Validate range
datetime_range.validate_range()?;
datetime_range.validate_max_duration(7)?; // Max 7 days
// Parse to UTC (handles timezone conversion automatically)
let start_utc = datetime_range.parse_start_utc()?;
let end_utc = datetime_range.parse_end_utc()?;
let events = Event::find_by_datetime_range(start_utc, end_utc).await?;
data!(events)
}
start_date=2024-12-19
- Date only formatend_date=2024-12-31
- Perfect for daily/monthly reports
start_datetime=2024-12-19T10:30:00Z
- UTC formatstart_datetime=2024-12-19T10:30:00+08:00
- Timezone awarestart_datetime=2024-12-19T10:30:00-05:00
- Negative timezone
use avinapi::{ValidatedJson, validation::*};
#[derive(Deserialize, Validate, ToSchema)]
struct CreateUserRequest {
#[validate(length(min = 1, max = 100))]
name: String,
#[validate(email)]
email: String,
#[validate(custom = "validate_password_strength")]
password: String,
}
async fn create_user(
ValidatedJson(request): ValidatedJson<CreateUserRequest>
) -> JsonResult<User> {
// Request is guaranteed to be valid here
let user = User::create(request.name, request.email, request.password).await?;
data!(user)
}
Important: This library follows a unified response pattern where all endpoints return HTTP 200 status codes, with business logic status indicated by the code
field in the JSON response body.
All endpoints return a consistent JSON structure:
{
"code": "SUCCESS", // Business status code
"data": { ... }, // Response data
"message": null // Optional message
}
Error responses (still HTTP 200):
{
"code": "VALIDATION_ERROR", // Business error code
"data": null,
"message": "name: Name is required, email: Invalid email format"
}
SUCCESS
- Operation completed successfullyVALIDATION_ERROR
- Input validation failedAUTHENTICATION_ERROR
- Authentication required or failedAUTHORIZATION_ERROR
- Insufficient permissionsNOT_FOUND
- Requested resource not foundCONFLICT
- Resource conflict (e.g., duplicate email)INTERNAL_ERROR
- Server-side errorDATABASE_ERROR
- Database operation failedNETWORK_ERROR
- External service call failed
Paginated responses:
{
"code": "SUCCESS",
"data": {
"data": [...],
"meta": {
"current_page": 1,
"per_page": 20,
"total_items": 150,
"total_pages": 8,
"has_next_page": true,
"has_prev_page": false
}
},
"message": null
}
# Minimal setup
avinapi = { version = "0.1.0", default-features = false }
# With specific features
avinapi = { version = "0.1.0", features = ["axum", "sqlx"] }
# Full features
avinapi = { version = "0.1.0", features = ["axum", "sqlx", "jwt"] }
The library respects these environment variables:
RUST_LOG
- Logging level (uses tracing)DATABASE_URL
- Database connection (when using sqlx feature)
avinapi/
βββ src/
β βββ error/ # Error handling and HTTP status mapping
β βββ middleware/ # Common middleware utilities
β βββ query/ # Query parameter types and validation
β β βββ date_range.rs # Simple date filtering
β β βββ datetime_range.rs # Precise datetime filtering
β β βββ pagination.rs # Page-based pagination
β β βββ sort.rs # Multi-field sorting
β βββ response/ # Standardized response types
β βββ validation/ # JSON validation and custom validators
β βββ prelude.rs # Common imports
βββ examples/ # Usage examples
βββ tests/ # Integration tests
Run the included examples:
# Basic query parameter usage
cargo run --example query_parameters
# DateTime range handling with different formats
cargo run --example datetime_range_usage
Then visit:
http://localhost:3000/users?page=1&per_page=10
http://localhost:3000/calendar?start_datetime=2024-12-19T10:30:00+08:00
use avinapi::prelude::*;
use axum::{Router, middleware};
let app = Router::new()
.route("/api/users", get(list_users))
.layer(middleware::from_fn(request_id_middleware))
.layer(cors_layer());
use avinapi::query::*;
use sqlx::{PgPool, query_as};
async fn get_orders(
pool: &PgPool,
datetime_range: &DateTimeRangeQuery,
pagination: &PaginationQuery,
) -> Result<Vec<Order>, sqlx::Error> {
let (start_utc, end_utc) = datetime_range.parse_utc_range();
query_as!(
Order,
r#"
SELECT * FROM orders
WHERE ($1::timestamptz IS NULL OR created_at >= $1)
AND ($2::timestamptz IS NULL OR created_at < $2)
ORDER BY created_at DESC
LIMIT $3 OFFSET $4
"#,
start_utc,
end_utc,
pagination.get_limit() as i64,
pagination.get_offset() as i64
)
.fetch_all(pool)
.await
}
# Run all tests
cargo test
# Run with all features
cargo test --all-features
# Run examples
cargo test --examples
# Run doc tests
cargo test --doc
This project follows Semantic Versioning:
- MAJOR version for incompatible API changes
- MINOR version for backwards-compatible functionality additions
- PATCH version for backwards-compatible bug fixes
Contributions are welcome! Please feel free to submit a Pull Request.
git clone https://github.com/AvinasiLabs/avinapi
cd avinapi
cargo build --all-features
cargo test --all-features
This project is licensed under either of
- Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
- Axum - Modern web framework
- Chrono - Date and time library
- Serde - Serialization framework
- Validator - Data validation
- utoipa - OpenAPI documentation
Created by @lilhammer111 | Latte Team, Avinasi Labs