A fully automated OpenAPI engine for Axum with zero-config route and schema discovery.
Vespera is a fully automated OpenAPI engine for Axum — delivering a FastAPI-like developer experience to the Rust ecosystem.
It automatically discovers routes, imports handlers and schemas, and generates a complete OpenAPI 3.1 specification with zero configuration.
Just write your Axum API.
Vespera handles the rest.
Automatically scans Axum routers and submodules to detect all registered routes.
Automatically pulls in handlers, request/response types, and data models into the OpenAPI spec.
Generates a complete OpenAPI 3.1 document from:
- Routes
- Extractors
- Parameters
- Request bodies
- Response bodies
- Rust data structures (Serde)
Rust types are converted into JSON Schema with full type fidelity.
Automatically generates and serves Swagger UI documentation when docs_url is specified, providing interactive API exploration.
Built specifically for Axum's architecture while offering the productivity of modern API frameworks.
use axum::{Router, routing::get};
use vespera::vespera;
use axum::Json;
async fn health() -> &'static str {
"ok"
}
async fn get_user(id: u32) -> Json<User> {
Json(User { id, name: "Alice".into() })
}
#[tokio::main]
async fn main() {
dotenv().ok();
let config = Config::from_env();
let port = config.port;
let db = create_db_connection(&config.database_url).await;
let state = AppState { db, config };
let app = vespera!(
openapi = "openapi.json",
title = "My API",
version = "1.0.0",
docs_url = "/docs"
)
.with_state(state)
.layer(
CorsLayer::new()
.allow_origin("http://localhost:3000".parse::<HeaderValue>().unwrap())
.allow_methods([
Method::GET,
Method::POST,
Method::PUT,
Method::DELETE,
Method::OPTIONS,
])
.allow_headers([
axum::http::header::CONTENT_TYPE,
axum::http::header::AUTHORIZATION,
]),
);
let addr = SocketAddr::from(([0, 0, 0, 0], port));
println!("API server is running on port {}", port);
println!("Swagger UI available at http://localhost:{}/docs", port);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}Add the following to your Cargo.toml:
[dependencies]
vespera = "0.1.0"
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }Create a src/routes folder in your project root and write route handlers:
src/
├── main.rs
└── routes/
├── mod.rs
├── users.rs
└── posts.rs
src/routes/users.rs:
use axum::{Json, Path, State};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct User {
pub id: u32,
pub name: String,
}
/// `/users/{id}` - default method is get
#[vespera::route(path = "/{id}")]
pub async fn get_user(
State(state): State<AppState>,
Path(id): Path<i32>) -> Json<User> {
Json(User {
id,
name: "Alice".into(),
})
}
/// /users
#[vespera::route(method = "post")]
pub async fn create_user(Json(user): Json<User>) -> Json<User> {
Json(user)
}src/routes/mod.rs:
pub mod users;
pub mod posts;src/main.rs:
use vespera::vespera;
#[tokio::main]
async fn main() {
// Basic usage: scans "routes" folder by default
let app = vespera!();
// Or with OpenAPI and Swagger UI support
let app = vespera!(
openapi = "openapi.json",
title = "My API",
version = "1.0.0",
docs_url = "/docs"
);
// Or specify a custom folder: vespera!("api")
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], 3000));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
println!("Server running on http://localhost:3000");
println!("Swagger UI available at http://localhost:3000/docs");
axum::serve(listener, app).await.unwrap();
}Automatically scans routes and generates an Axum Router with optional OpenAPI and Swagger UI support.
// Basic usage: scans "routes" folder
let app = vespera!();
// Specify a custom folder
let app = vespera!("api");
// With OpenAPI JSON file generation
let app = vespera!(
openapi = "openapi.json"
);
// With OpenAPI and Swagger UI
let app = vespera!(
openapi = "openapi.json",
docs_url = "/docs"
);
// Full configuration with all parameters
let app = vespera!(
dir = "routes", // Route folder name (default: "routes")
openapi = "openapi.json", // OpenAPI JSON file path (optional)
title = "My API", // API title (optional, default: "API")
version = "1.0.0", // API version (optional, default: "1.0.0")
docs_url = "/docs" // Swagger UI documentation URL (optional)
);-
dir: Route folder name (default:"routes")- You can also specify it directly as a string literal:
vespera!("api")
- You can also specify it directly as a string literal:
-
openapi: OpenAPI JSON file path (optional)- If specified, an OpenAPI 3.1 spec is generated at compile time and writes an
openapi.jsonfile to the specified path - Example:
openapi = "openapi.json"→ createsopenapi.jsonfile in project root - Example:
openapi = "docs/api.json"→ createsdocs/api.jsonfile
- If specified, an OpenAPI 3.1 spec is generated at compile time and writes an
-
title: API title (optional, default:"API")- Used in the
info.titlefield of the OpenAPI document
- Used in the
-
version: API version (optional, default:"1.0.0")- Used in the
info.versionfield of the OpenAPI document
- Used in the
-
docs_url: Swagger UI documentation URL (optional)- If specified, you can view the API documentation through Swagger UI at that path
- Example: Setting
docs_url = "/docs"allows viewing documentation athttp://localhost:3000/docs
Specify HTTP method and path for handler functions.
// GET request
#[vespera::route(get)]
pub async fn list_users() -> Json<Vec<User>> {
// ...
}
// POST request (custom path)
#[vespera::route(post, path = "/users")]
pub async fn create_user(Json(user): Json<User>) -> Json<User> {
// ...
}
// Path parameter support
#[vespera::route(get, path = "/users/:id")]
pub async fn get_user(id: u32) -> Json<User> {
// ...
}GETPOSTPUTPATCHDELETEHEADOPTIONS
When you specify the openapi parameter in the vespera! macro, an OpenAPI 3.1 spec is automatically generated at compile time and writes a file to the specified path.
let app = vespera!(
openapi = "openapi.json", // Creates openapi.json file at this path
title = "My API", // API title
version = "1.0.0", // API version
docs_url = "/docs" // Swagger UI URL (optional)
);With this configuration:
- An OpenAPI JSON file is automatically generated at the specified path during compilation
openapi = "openapi.json"→ createsopenapi.jsonfile in project rootopenapi = "docs/api.json"→ createsdocs/api.jsonfile
- If you specify
docs_url, you can view the API documentation through Swagger UI at that path - The OpenAPI spec is automatically generated by analyzing routes, handlers, and request/response types
Note: The build.rs file is no longer needed. The vespera! macro automatically handles it at compile time.
File structure is automatically converted to URL paths:
routes/
├── users.rs → /users
├── posts.rs → /posts
└── admin/
└── users.rs → /admin/users
vespera/
├── Cargo.toml
├── README.md
└── crates/
└── vespera/
├── Cargo.toml
└── src/
├── lib.rs # Main macro definitions
├── args.rs # Macro argument parsing
├── file_utils.rs # File system utilities
├── method.rs # HTTP method definitions
└── route/
├── mod.rs
└── utils.rs # Route information extraction
-
Compile-Time Scanning: The
vespera!macro scans the specified folder to discover all route handlers. -
Attribute Parsing: Extracts HTTP method and path information from each handler's
#[route]attribute. -
Code Generation: Automatically generates Axum Router code based on discovered routes.
-
Type Safety: Leverages Rust's type system to ensure all routes are correctly registered at compile time.
Contributions are welcome! Please open an issue or submit a Pull Request.
# Clone the repository
git clone https://github.com/yourusername/vespera.git
cd vespera
# Build
cargo build
# Run tests
cargo testThis project is licensed under the Apache 2.0 License. See the LICENSE file for details.
- Automatic routes importing
- Automatic OpenAPI 3.1 spec generation (via
vespera!macro) - Automatic request/response schema extraction
- Swagger UI integration
- Support for more Axum extractors
Vespera is inspired by FastAPI’s developer experience and also takes inspiration from Next.js, all designed for the Rust ecosystem.