sword 0.1.5

Axum based web framework prototype
Documentation

โœจ Features

  • ๐Ÿ›ฃ๏ธ Macro-based routing - Clean and intuitive route definitions
  • ๐Ÿ” Complex query parameters - Ready for advanced parameter handling
  • ๐Ÿ“„ 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

๐Ÿ› ๏ธ Usage

Add to your Cargo.toml

[dependencies]
sword = "0.1.5"

# Additional dependencies for features

# validation features:
validator = { version = "0.20.0", features = ["derive"] }

# dependency injection features:
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 {}

#[controller_impl]
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 {}

#[controller_impl]
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(())
}

With Data Validation

use serde::{Deserialize, Serialize};
use sword::prelude::*;
use sword::web::HttpResult;
use validator::Validate;

#[derive(Serialize, Deserialize, Validate)]
struct UserQuery {
    #[validate(range(message = "Page must be between 1 and 1000", min = 1, max = 1000))]
    page: u32,
    #[validate(range(message = "Limit must be between 1 and 100", min = 1, max = 100))]
    limit: u32,
}

#[derive(Serialize, Deserialize, Validate)]
struct CreateUserRequest {
    #[validate(length(min = 1, message = "Name must not be empty"))]
    name: String,
    #[validate(email(message = "Invalid email format"))]
    email: String,
}

#[controller("/users")]
struct UserController {}

#[controller_impl]
impl UserController {
    #[get("/")]
    async fn get_users(ctx: Context) -> HttpResult<HttpResponse> {
        let query = ctx.validated_query::<UserQuery>()?;
        
        Ok(HttpResponse::Ok()
            .data(format!("Page: {}, Limit: {}", query.page, query.limit))
            .message("Users retrieved successfully"))
    }

    #[post("/")]
    async fn create_user(ctx: Context) -> HttpResult<HttpResponse> {
        let user = ctx.validated_body::<CreateUserRequest>()?;

        Ok(HttpResponse::Ok()
            .data(user)
            .message("User created successfully"))
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    Application::builder()
        .controller::<UserController>()
        .run("0.0.0.0:8080")
        .await?;
}

More Examples

See the examples directory for more advanced usage.

Currently working on

  • โœ…๐Ÿ“ฑ Add Application struct
  • โœ… ๐Ÿ—๏ธ Add Application Context
  • โœ… ๐Ÿ”’ Add Middleware support
  • โœ… ๐Ÿ’‰ Add Dependency Injection support based on shaku crate
  • โš™๏ธ Add config file support

๐Ÿ“‹ Roadmap

  • ๐Ÿ“ Add File - FormData support
  • ๐Ÿงช Add more tests
  • ๐Ÿ“š Add more documentation
  • ๐Ÿ› ๏ธ CLI Command line interface for code-generation (templates)