
After years of building enterprise APIs at companies like Apple, Salesforce, and Visa, I kept hitting the same wall with Rust: setting up a new web service takes hours of wiring boilerplate.
You need to configure:
- Database connections and pooling
- Request validation
- Error handling patterns
- OpenAPI documentation
- Logging and tracing
- CORS
- Configuration management
- Project structure
Everyone does it differently. Every new service is a fresh adventure in dependency wiring.
The FastAPI Moment
Python developers had this problem too, until FastAPI came along. Suddenly, you could write:
@app.post("/users")
async def create_user(user: UserCreate):
return user
And get automatic validation, serialization, and OpenAPI docs. That's the experience Rust deserves.
Enter rapid-rs
Today, I'm excited to launch rapid-rs - a zero-config web framework that brings FastAPI's developer experience to Rust.
Your First API in 3 Commands
# Install the CLI
cargo install rapid-rs-cli
# Create a project
rapid new myapi
# Run it
cd myapi && cargo run
Visit http://localhost:3000/docs and you've got a working API with Swagger UI. That's it.
What Does the Code Look Like?
Here's a complete, production-ready endpoint:
use rapid_rs::prelude::*;
#[derive(Deserialize, Validate)]
struct CreateUser {
#[validate(email)]
email: String,
#[validate(length(min = 2, max = 100))]
name: String,
}
#[derive(Serialize)]
struct User {
id: Uuid,
email: String,
name: String,
created_at: DateTime<Utc>,
}
async fn create_user(
ValidatedJson(payload): ValidatedJson<CreateUser>
) -> ApiResult<User> {
let user = User {
id: Uuid::new_v4(),
email: payload.email,
name: payload.name,
created_at: Utc::now(),
};
Ok(Json(user))
}
#[tokio::main]
async fn main() {
App::new()
.auto_configure() // Magic happens here
.route("/users", post(create_user))
.run()
.await
.unwrap();
}
What Does .auto_configure() Give You?
✅ Configuration Management - Loads from TOML files + environment variables
✅ Request Validation - With helpful error messages
✅ Structured Logging - With request correlation IDs
✅ CORS - Sensible defaults, fully configurable
✅ Health Checks - /health endpoint ready
✅ OpenAPI/Swagger - Auto-generated docs at /docs
✅ Error Handling - Unified error responses
All working together, out of the box.
Why Not Just Use Axum?
Great question! Axum is excellent - in fact, rapid-rs is built on top of Axum.
The difference is philosophy:
- Axum gives you powerful primitives (like Express.js)
- rapid-rs gives you conventions and batteries (like NestJS or FastAPI)
Think of it this way:
Axum : Express.js
rapid-rs : NestJS/FastAPI
You can still use all Axum patterns when you need them. rapid-rs just makes the common path faster.
Real-World Example: CRUD API
Here's a complete user management API with validation:
use rapid_rs::prelude::*;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
type Database = Arc<Mutex<HashMap<Uuid, User>>>;
// Create
async fn create_user(
State(db): State<Database>,
ValidatedJson(payload): ValidatedJson<CreateUser>,
) -> ApiResult<User> {
let user = User {
id: Uuid::new_v4(),
email: payload.email,
name: payload.name,
created_at: Utc::now(),
};
db.lock().unwrap().insert(user.id, user.clone());
Ok(Json(user))
}
// Read
async fn get_user(
State(db): State<Database>,
Path(id): Path<Uuid>,
) -> Result<Json<User>, ApiError> {
let user = db
.lock()
.unwrap()
.get(&id)
.ok_or_else(|| ApiError::NotFound(
format!("User {} not found", id)
))?
.clone();
Ok(Json(user))
}
// List
async fn list_users(
State(db): State<Database>
) -> ApiResult<Vec<User>> {
let users: Vec<User> = db
.lock()
.unwrap()
.values()
.cloned()
.collect();
Ok(Json(users))
}
fn routes() -> Router<Database> {
Router::new()
.route("/users", post(create_user))
.route("/users", get(list_users))
.route("/users/:id", get(get_user))
}
#[tokio::main]
async fn main() {
let db = Arc::new(Mutex::new(HashMap::new()));
App::new()
.auto_configure()
.mount(routes().with_state(db))
.run()
.await
.unwrap();
}
Run it and you get:
- ✅ All endpoints working
- ✅ Request validation
- ✅ Proper error responses
- ✅ Swagger UI documentation
- ✅ Health checks
- ✅ Request tracing
Configuration: The Right Way
Configuration loads from multiple sources (in priority order):
-
config/default.toml- Base settings -
config/local.toml- Local overrides (gitignored) - Environment variables -
APP__SERVER__PORT=8080
# config/default.toml
[server]
host = "0.0.0.0"
port = 3000
[database]
url = "postgres://localhost/mydb"
max_connections = 10
Override for production:
APP__SERVER__PORT=8080 cargo run
Type-safe, environment-aware, and follows the 12-factor app methodology.
Error Handling Done Right
Request validation errors automatically return helpful responses:
{
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"errors": [
{
"field": "email",
"message": "Invalid email format"
},
{
"field": "name",
"message": "Name must be between 2 and 100 characters"
}
]
}
Custom errors are just as easy:
#[derive(Debug, Error, ApiError)]
pub enum MyError {
#[error("User not found")]
#[status(NOT_FOUND)]
NotFound,
#[error("Insufficient credits")]
#[status(PAYMENT_REQUIRED)]
InsufficientCredits,
}
Development Experience
Hot reload is built-in:
rapid dev # Wraps cargo-watch
Project scaffolding creates everything you need:
rapid new myapi --template rest-api
Gets you:
myapi/
├── src/
│ └── main.rs # Working example
├── config/
│ ├── default.toml # Base config
│ └── local.toml # Your overrides
├── Cargo.toml # All dependencies ready
└── README.md # Usage guide
Performance: Why Rust?
Let's be honest about the elephant in the room: why not just use FastAPI or Spring Boot?
Because Rust gives you:
- 10-100x faster than Python/Node.js
- Memory safety without garbage collection
- Zero-cost abstractions - pay only for what you use
- Compile-time guarantees - catch bugs before production
- Fearless concurrency - async by default
With rapid-rs, you don't trade productivity for performance. You get both.
Comparison Table
| Feature | FastAPI | Spring Boot | rapid-rs |
|---|---|---|---|
| Type Safety | ❌ Runtime | ⚠️ Runtime | ✅ Compile-time |
| Auto OpenAPI | ✅ | ✅ | ✅ |
| Hot Reload | ✅ | ✅ | ✅ |
| Zero Config | ✅ | ✅ | ✅ |
| Performance | ⚠️ Good | ⚠️ Good | ✅ Excellent |
| Memory Safety | ❌ | ❌ | ✅ Guaranteed |
| Async by Default | ⚠️ | ❌ | ✅ |
What's Next?
This is v0.1.0 - an MVP with the core features working. Here's the roadmap:
Phase 2 (Next 2-3 weeks):
- Authentication & Authorization (JWT, sessions)
- Database migrations management
- Testing utilities (
TestApp::new().spawn()) - More templates (GraphQL, gRPC)
Phase 3 (Future):
- Background jobs
- Multi-tenancy support
- Feature flags
- Admin panel generation
Try It Today
cargo install rapid-rs-cli
rapid new myapi
cd myapi && cargo run
Visit http://localhost:3000/docs and you're live!
Get Involved
rapid-rs is open source (MIT/Apache-2.0) and looking for contributors:
- 🌟 GitHub
- 💬 Issues and discussions welcome!
Why I Built This
At companies like Apple and Salesforce, I saw the same pattern: teams spending days wiring infrastructure before writing business logic. Python teams had FastAPI. Java teams had Spring Boot. Rust teams deserved better.
rapid-rs is my attempt to bring that same productivity to Rust, without sacrificing any of Rust's strengths.
If you've ever thought "I wish Rust web development was easier," this is for you.
What do you think? Try it out and let me know! I'm actively working on this and would love your feedback.
⭐ Star the repo: https://github.com/ashishjsharda/rapid-rs
💬 Questions? Open an issue or comment below!
Top comments (1)
Good overview on explanation and reasons. I haven't used Rust, but I feel like I've seen frameworks with less boilerplate?
Mainly commenting to say it feels disingenuous to mark Python (FastAPI) memory safety as ❌.