rapina-cli 0.7.0

CLI tool for Rapina web framework - create and run Rapina projects
rapina-cli-0.7.0 is not a library.

use rapina::prelude::*;

#[get("/users")]
async fn list_users(db: Db) -> Result<Json<Vec<User>>> {
    let users = User::find_all(db.conn()).await?;
    Ok(Json(users))
}

#[post("/users")]
async fn create_user(input: Validated<Json<CreateUser>>, db: Db) -> Result<Json<User>> {
    let user = User::create(db.conn(), input.into_inner()).await?;
    Ok(Json(user))
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    Rapina::new()
        .discover()
        .listen("127.0.0.1:3000")
        .await
}

No router configuration. No manual wiring. Annotate your handlers, call .discover(), ship.

Get started

cargo install rapina-cli
rapina new my-app
cd my-app
rapina dev

Your API is running. That's it.

What you get

Auto-discovery — annotate handlers with #[get], #[post], #[put], #[delete]. Rapina finds them at startup. Your main.rs stays three lines.

Database from day one — define your schema declaratively. author: User becomes a foreign key. posts: Vec<Post> becomes a relationship. SeaORM entities are auto-generated.

schema! {
    User {
        name: String,
        email: String,
        posts: Vec<Post>
    }

    Post {
        title: String,
        body: String,
        author: User
    }
}

Auth that works — JWT authentication, protected by default. Mark public routes with #[public]. Access the current user with the CurrentUser extractor.

#[public]
#[post("/login")]
async fn login(body: Json<LoginRequest>, auth: State<AuthConfig>) -> Result<Json<TokenResponse>> {
    let token = auth.create_token(&body.username)?;
    Ok(Json(TokenResponse::new(token, auth.expiration())))
}

#[get("/me")]
async fn me(user: CurrentUser) -> Json<UserResponse> {
    Json(UserResponse { id: user.id })
}

CRUD in one command — scaffold an entire resource with handlers, DTOs, errors, schema, and migration.

rapina add resource Post

OpenAPI built-in — spec generation, breaking change detection, and validation from the CLI.

rapina openapi export -o openapi.json
rapina openapi diff --base main

Production middleware — rate limiting, compression, CORS, and Prometheus metrics out of the box.

Rapina::new()
    .with_rate_limit(RateLimitConfig::per_minute(100))
    .with_compression(CompressionConfig::default())
    .with_cors(CorsConfig::permissive())
    .discover()
    .listen("0.0.0.0:3000")
    .await

CLI for everything — create, develop, test, inspect, generate.

rapina new my-app              # Scaffold a new project
rapina dev                     # Dev server with hot reload
rapina test --coverage         # Tests with coverage report
rapina test --watch            # Watch mode
rapina routes                  # List all routes
rapina doctor                  # Health checks and diagnostics
rapina migrate new             # Create a migration
rapina add resource Post       # Full CRUD scaffolding

10 extractors

Everything you need to pick apart a request, type-safe and compile-time checked.

Json<T> · Form<T> · Path<T> · Query<T> · Headers · Cookie<T> · State<T> · Validated<T> · CurrentUser · Db

Standardized errors

Every error includes a trace_id. No more guessing in production.

{
  "error": { "code": "NOT_FOUND", "message": "user not found" },
  "trace_id": "550e8400-e29b-41d4-a716-446655440000"
}

Project status

Rapina is in active development. We ship fast and we ship often — 9 releases since January 2026. The API is stabilizing but breaking changes may still occur before 1.0.0.

See the roadmap for what's coming next.

Documentation

Full documentation at userapina.com

Contributing

Contributions are welcome. Check out the open issues or join us on Discord.

License

MIT