✨ Features
- 🛣️ Macro-based routing - Clean and intuitive route definitions
- 📄 JSON-first design - Built with JSON formats as priority
- ✅ Built-in validation - Support with
serde
and validator
crates
- 🌐 RFC-compliant HTTP responses - Using
axum_responses
crate
- � Express-Like - It provides a
Context
object with utility methods for request handling
- �💉 Dependency Injection - Built-in DI support using
shaku
crate
- 🧩 Middleware support - Easily add middleware to routes or controllers
- 🚀 Asynchronous by default - Built on top of
axum
and tokio
🛠️ Usage
Add to your Cargo.toml
[dependencies]
sword = "0.1.7"
tokio = { version = "1.47.1", features = ["full"] }
validator = { version = "0.20.0", features = ["derive"] }
serde = { version = "*", features = ["derive"] }
serde_json = "*"
shaku = { version = "0.6.2", features = ["derive"] }
async-trait = "0.1.88"
Basic web server
use sword::prelude::*;
use sword::web::HttpResult;
#[controller("/")]
struct AppController {}
#[routes]
impl AppController {
#[get("/")]
async fn get_data() -> HttpResponse {
let data = vec![
"This is a basic web server",
"It serves static data",
"You can extend it with more routes",
];
HttpResponse::Ok().data(data)
}
#[get("/hello")]
async fn hello() -> HttpResponse {
HttpResponse::Ok().data("Hello, World!")
}
#[post("/submit")]
async fn submit_data(ctx: Context) -> HttpResult<HttpResponse> {
let body = ctx.body::<serde_json::Value>()?;
Ok(HttpResponse::Ok()
.data(body)
.message("Data submitted successfully"))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Application::builder()?
.controller::<AppController>()
.run("0.0.0.0:8080")
.await?;
Ok(())
}
With Middleware
use serde_json::json;
use sword::prelude::*;
use sword::web::HttpResult;
struct LoggingMiddleware;
impl Middleware for LoggingMiddleware {
async fn handle(mut ctx: Context, next: Next) -> MiddlewareResult {
println!("Request: {} {}", ctx.method(), ctx.uri());
ctx.extensions.insert::<String>("middleware_data".to_string());
next!(ctx, next)
}
}
#[controller("/api")]
struct AppController {}
#[routes]
impl AppController {
#[get("/hello")]
#[middleware(LoggingMiddleware)]
async fn hello(ctx: Context) -> HttpResult<HttpResponse> {
let middleware_data = ctx.extensions
.get::<String>()
.cloned()
.unwrap_or_default();
Ok(HttpResponse::Ok().data(json!({
"message": "Hello from middleware!",
"middleware_data": middleware_data
})))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Application::builder()?
.controller::<AppController>()
.run("0.0.0.0:8080")
.await?;
Ok(())
}
Known Issues
- Body size limit errors return a
400 Bad Request
instead of `413 Payload Too Large. It will be fixed in future releases.
More Examples
See the examples directory for more advanced usage.
Hot reloading
In the case of use hot reloading, you need to install dioxus-cli
:
cargo install --git https://github.com/DioxusLabs/dioxus.git dioxus-cli
Then run the server with:
dx serve --hot-patch --example hot_reloading