Which Rust Web Framework Wins in 2022? actix-web vs warp vs axum
This article compares three popular Rust web frameworks—actix-web, warp, and axum—by examining their versions, download counts, ecosystem metrics, performance considerations, and code examples for JSON handling, routing, middleware, and shared state, ultimately recommending the best fit for different project sizes.
Guide: This article selects several excellent frameworks for comparison (including example code).
Framework List
The list was compiled in February 2022.
Framework Version Total Downloads Description
actix-web 4.0.0-rc.3 5,134,720 A powerful, practical, high‑performance Rust web framework
warp 0.3.2 4,114,095 Build extremely efficient web services
axum 0.4.5 235,150 Focuses on ergonomics and modularityNote (as of April 2023): actix-web v4.3.1 has 10,698,368 downloads, axum v0.6.16 has 11,592,805 downloads.
Other notable frameworks (not included in this evaluation) include rocket, hyper, tide, rouille, iron, etc., which were omitted due to being too young, low‑level, lacking async support, or no longer maintained.
Latest framework list: https://www.arewewebyet.org/topics/frameworks/
Performance
All three frameworks provide sufficient performance for typical applications, so detailed micro‑benchmarks are unnecessary unless you need to handle millions of requests per second.
Ecosystem and Community
A good web framework needs an active community and a rich set of third‑party libraries.
Framework Github Stars Third‑party libs Official examples Open Issues Closed Issues
actix-web ~13.3k ~500 57 95 1234
warp ~6k ~184 24 134 421
axum ~3.6k ~50 36 6 192Winner: actix-web has the best ecosystem and community, also ranking highly in the TechEmpower benchmarks.
However, axum benefits from rapid ecosystem growth as it is backed by the Tokio team.
JSON Deserialization
actix-web
#[derive(Debug, Serialize, Deserialize)]
struct Hello {
message: String,
}
async fn index(item: web::Json<Hello>) -> HttpResponse {
HttpResponse::Ok().json(item.message) // <- send response
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(web::resource("/").route(web::post().to(index)))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}warp
#[derive(Debug, Serialize, Deserialize)]
struct Hello {
message: String,
}
async fn index(item: Hello) -> Result<impl warp::Reply, Infallible> {
Ok(warp::reply::json(&hello.message))
}
#[tokio::main]
async fn main() {
let promote = warp::post()
.and(warp::body::json())
.map(index);
warp::serve(promote).run(([127, 0, 0, 1], 8080)).await
}axum
#[derive(Debug, Serialize, Deserialize)]
struct Hello {
message: String,
}
async fn index(item: Json<Hello>) -> impl IntoResponse {
Json(item.message)
}
#[tokio::main]
async fn main() {
let app = Router::new().route("/", post(index));
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}All three frameworks provide simple generic JSON deserialization, but axum and actix-web are considered more convenient for automatic handling.
Routing
actix-web
fn main() {
App::new()
.service(web::resource("/").route(web::get().to(api::list)))
.service(web::resource("/todo").route(web::post().to(api::create)))
.service(web::resource("/todo/{id}")
.route(web::post().to(api::update))
.route(web::delete().to(api::delete)));
}warp
pub fn todos() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
todos_list(db.clone())
.or(todos_create(db.clone()))
.or(todos_update(db.clone()))
.or(todos_delete(db))
}
pub fn todos_list() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("todos")
.and(warp::get())
.and(warp::query::<ListOptions>())
.and_then(handlers::list_todos)
}
// similar definitions for create, update, delete ...axum
let app = Router::new()
.route("/todos", get(todos_list).post(todos_create))
.route("/todos/:id", patch(todos_update).delete(todos_delete));Winner for routing simplicity: axum, followed by actix-web, then warp (which uses a more functional style).
Middleware
actix-web
pub struct SayHi;
impl<S, B> Transform<S, ServiceRequest> for SayHi
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type InitError = ();
type Transform = SayHiMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(SayHiMiddleware { service }))
}
}
pub struct SayHiMiddleware<S> { service: S }
impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>;
fn call(&self, req: ServiceRequest) -> Self::Future {
println!("before");
let fut = self.service.call(req);
Box::pin(async move {
let res = fut.await?;
println!("after");
Ok(res)
})
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
App::new()
.wrap(SayHi)
.service(web::resource("/").to(|| async { "Hello, middleware!" }))
.await
}warp
pub fn json_body<T: DeserializeOwned + Send>() -> impl Filter<Extract = (T,), Error = warp::Rejection> + Clone {
warp::body::content_length_limit(1024 * 16).and(warp::body::json())
}
fn main() {
let api = filters::todos(db);
warp::serve(api).run(([127, 0, 0, 1], 8080)).await;
}axum
#[derive(Clone)]
struct MyMiddleware<S> { inner: S }
impl<S> Service<Request<Body>> for MyMiddleware<S>
where
S: Service<Request<Body>, Response = Response> + Clone + Send + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
println!("before");
let mut inner = std::mem::replace(&mut self.inner, self.inner.clone());
Box::pin(async move {
let res = inner.call(req).await?;
println!("after");
Ok(res)
})
}
}
fn main() {
let app = Router::new()
.route("/", get(|| async { /* ... */ }))
.layer(layer_fn(|inner| MyMiddleware { inner }));
}Winner for middleware flexibility: warp.
Shared State
actix-web
struct State {}
async fn index(state: Data<Arc<State>>, req: HttpRequest) -> HttpResponse { /* ... */ }
#[actix_web::main]
async fn main() -> io::Result<()> {
let state = Arc::new(State {});
HttpServer::new(move || {
App::new()
.app_data(state.clone())
.service(web::resource("/").to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}warp
struct State {}
pub fn with_state(state: Arc<State>) -> impl Filter<Extract = (Arc<State>,), Error = std::convert::Infallible> + Clone {
warp::any().map(move || state.clone())
}
async fn create_job(state: Arc<AppState>) -> Result<impl warp::Reply, warp::Rejection> { /* ... */ }
fn main() {
let state = Arc::new(State {});
let api = api.and(warp::path("jobs"))
.and(warp::path::end())
.and(warp::post())
.and(with_state(state))
.and_then(create_job);
}axum
struct State {}
async fn handler(Extension(state): Extension<Arc<State>>) { /* ... */ }
fn main() {
let shared_state = Arc::new(State {});
let app = Router::new()
.route("/", get(handler))
.layer(AddExtensionLayer::new(shared_state));
}Result: a tie—designs are very similar and easy to use.
Conclusion
I prefer axum for its ergonomic API and Tokio backing, though its youth may deter some. For large projects, actix-web is the safest choice, which is why I used it for the Bloom project. For smaller projects, warp works well, offering raw performance and security via Hyper.
Ultimately, a solid architecture makes switching between frameworks straightforward—just pick one and start building.
References
Original article: https://kerkour.com/rust-web-framework-2022
Translation: https://github.com/mrxiaozhuox (YuKun Liu)
Rust Web Framework overview: https://rustmagazine.github.io/rust_magazine_2022/Q1/opensource/web.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
