DEV Community

Ashish Sharda
Ashish Sharda

Posted on

Introducing rapid-rs: Zero-Config Web Framework for Rust

rapid-rs
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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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):

  1. config/default.toml - Base settings
  2. config/local.toml - Local overrides (gitignored)
  3. 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
Enter fullscreen mode Exit fullscreen mode

Override for production:

APP__SERVER__PORT=8080 cargo run
Enter fullscreen mode Exit fullscreen mode

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"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

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,
}
Enter fullscreen mode Exit fullscreen mode

Development Experience

Hot reload is built-in:

rapid dev  # Wraps cargo-watch
Enter fullscreen mode Exit fullscreen mode

Project scaffolding creates everything you need:

rapid new myapi --template rest-api
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. 10-100x faster than Python/Node.js
  2. Memory safety without garbage collection
  3. Zero-cost abstractions - pay only for what you use
  4. Compile-time guarantees - catch bugs before production
  5. 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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
shredwheat profile image
Peter Shinners

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 ❌.