dev-five-git/vespera

A fully automated OpenAPI engine for Axum with zero-config route and schema discovery

15 stars1 forksUpdated Jan 26, 2026
npx skills add dev-five-git/vespera

README

Vespera

FastAPI-like developer experience for Rust. Zero-config OpenAPI 3.1 generation for Axum.

Crates.io Documentation License CI Codecov

// That's it. Swagger UI at /docs, OpenAPI at openapi.json
let app = vespera!(openapi = "openapi.json", docs_url = "/docs");

Why Vespera?

FeatureVesperaManual Approach
Route registrationAutomatic (file-based)Manual Router::new().route(...)
OpenAPI specGenerated at compile timeHand-written or runtime generation
Schema extractionFrom Rust typesManual JSON Schema
Swagger UIBuilt-inSeparate setup
Type safetyCompile-time verifiedRuntime errors

Quick Start

1. Add Dependencies

[dependencies]
vespera = "0.1"
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }

2. Create Route Handler

src/
├── main.rs
└── routes/
    └── users.rs

src/routes/users.rs:

use axum::{Json, Path};
use serde::{Deserialize, Serialize};
use vespera::Schema;

#[derive(Serialize, Deserialize, Schema)]
pub struct User {
    pub id: u32,
    pub name: String,
}

/// Get user by ID
#[vespera::route(get, path = "/{id}", tags = ["users"])]
pub async fn get_user(Path(id): Path<u32>) -> Json<User> {
    Json(User { id, name: "Alice".into() })
}

/// Create a new user
#[vespera::route(post, tags = ["users"])]
pub async fn create_user(Json(user): Json<User>) -> Json<User> {
    Json(user)
}

3. Setup Main

src/main.rs:

use vespera::vespera;

#[tokio::main]
async fn main() {
    let app = vespera!(
        openapi = "openapi.json",
        title = "My API",
        docs_url = "/docs"
    );

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    println!("Swagger UI: http://localhost:3000/docs");
    axum::serve(listener, app).await.unwrap();
}

4. Run

cargo run
# Open http://localhost:3000/docs

Core Concepts

File-Based Routing

File structure maps to URL paths automatically:

src/routes/
├── mod.rs           → /
├── users.rs         → /users
├── posts.rs         → /posts
└── admin/
    ├── mod.rs       → /admin
    └── stats.rs     → /admin/stats

Route Handlers

Handlers must be pub async fn with the #[vespera::route] attribute:

// GET /users (default method)
#[vespera::route]
pub async fn list_users() -> Json<Vec<User>> { ... }

// POST /users
#[vespera::route(post)]
pub async fn create_user(Json(user): Json<User>) -> Json<User> { ... }

// GET /users/{id}
#[vespera::route(get, path = "/{id}")]
pub async fn get_user(Path(id): Path<u32>) -> Json<User> { ... }

// Full options
#[vespera::route(put, path = "/{id}", tags = ["users"], description = "Update user")]
pub async fn update_user(...) -> ... { ... }

Schema Derivation

Derive Schema on types used in request/response bodies:

#[derive(Serialize, Deserialize, vespera::Schema)]
#[serde(rename_all = "camelCase")]  // Serde attributes are respected
pub struct CreateUserRequest {
    pub user_name: String,          // → "userName" in OpenAPI
    pub email: String,
    #[serde(default)]
    pub bio: Option<String>,        // Optional field
}

Supported Extractors

ExtractorOpenAPI Mapping
Path<T>Path parameters
Query<T>Query parameters
Json<T>Request body (application/json)
Form<T>Request body (form-urlencoded)
TypedHeader<T>Header parameters
State<T>Ignored (internal)

Error Handling

#[derive(Serialize, Schema)]
pub struct ApiError {
    pub message: String,
}

#[vespera::route(get, path = "/{id}")]
pub async fn get_user(Path(id): Path<u32>) -> Result<Json<User>, (StatusCode, Json<ApiError>)> {
    if id == 0 {
        return Err((StatusCode::NOT_FOUND, Json(ApiError { message: "Not found".into() })));
    }
    Ok(Json(User { id, name: "Alice".into() }))
}

vespera! Macro Reference

let app = vespera!(
    dir = "routes",                    // Route folder (default: "routes")
    openapi = "openapi.json",          // Output path (writes file at compile time)
    title = "My API",                  // OpenAPI info.title
    version = "1.0.0",                 // OpenAPI info.version (default: CARGO_PKG_VERSION)
    docs_url = "/docs",                // Swagger UI endpoint
    redoc_url = "/redoc

...
Read full README

Publisher

dev-five-gitdev-five-git

Statistics

Stars15
Forks1
Open Issues0
CreatedNov 24, 2025