<p align="center">
<img src="docs/static/images/logo.png" alt="Rapina" width="120" />
</p>
<h1 align="center">Rapina</h1>
<p align="center">
<strong>A Rust web framework for APIs. So simple it feels like cheating.</strong>
</p>
<p align="center">
<a href="https://crates.io/crates/rapina"><img src="https://img.shields.io/crates/v/rapina.svg" alt="Crates.io"></a>
<a href="https://docs.rs/rapina"><img src="https://docs.rs/rapina/badge.svg" alt="Documentation"></a>
<a href="https://github.com/arferreira/rapina/actions/workflows/ci.yml"><img src="https://github.com/arferreira/rapina/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="https://discord.gg/Z4ww64YBQj"><img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" alt="Discord"></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
</p>
---
```rust
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
```bash
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.
```rust
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.
```rust
#[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.
```bash
rapina add resource Post
```
**OpenAPI built-in** — spec generation, breaking change detection, and validation from the CLI.
```bash
rapina openapi export -o openapi.json
rapina openapi diff --base main
```
**Production middleware** — rate limiting, compression, CORS, and Prometheus metrics out of the box.
```rust
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.
```bash
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.
```json
{
"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](https://userapina.com/roadmap/) for what's coming next.
## Documentation
Full documentation at [userapina.com](https://userapina.com/)
- [Getting Started](https://userapina.com/guide/getting-started/)
- [Database](https://userapina.com/guide/database/)
- [Authentication](https://userapina.com/guide/authentication/)
- [CLI Reference](https://userapina.com/cli/)
## Contributing
Contributions are welcome. Check out the [open issues](https://github.com/arferreira/rapina/issues) or join us on [Discord](https://discord.gg/Z4ww64YBQj).
## License
MIT