# Rust na backendzie webowym: Actix Web vs Axum - kompleksowe porównanie i pytania rekrutacyjne 2026 > Dogłębna analiza frameworków Actix Web 4.13 i Axum 0.8 dla backendu w Rust. Porównanie architektur runtime, benchmarki wydajności, wzorce ekstraktorów, middleware Tower, integracja z SQLx oraz kluczowe pytania na rozmowy kwalifikacyjne w 2026 roku. - Published: 2026-05-27 - Updated: 2026-05-27 - Author: SharpSkill - Tags: rust, actix-web, axum, web-framework, backend, comparison - Reading time: 10 min --- Ekosystem webowy Rust osiągnął w 2026 roku punkt, w którym wybór frameworka backendowego przestał być eksperymentem, a stał się decyzją architektoniczną o wymiernych konsekwencjach produkcyjnych. Dwa frameworki zdominowały krajobraz: [Actix Web](https://actix.rs/) w wersji 4.13 oraz [Axum](https://github.com/tokio-rs/axum) w wersji 0.8. Każdy z nich reprezentuje odmienną filozofię projektową — od modelu wątków po architekturę middleware — i każdy z nich pojawia się coraz częściej w pytaniach technicznych na rozmowach kwalifikacyjnych dotyczących stanowisk backendowych. > **Kluczowy wniosek** > > Actix Web 4.13 zapewnia 10-15% wyższą przepustowość dzięki modelowi przypiętych wątków (pinned-thread). Axum 0.8 oferuje natywne async traits, pełną kompatybilność z ekosystemem Tower i prostszą krzywą nauki. W przypadku nowych projektów webowych w Rust rozpoczynanych w 2026 roku Axum stanowi rozsądny wybór domyślny, o ile specyficzne wymagania wydajnościowe nie przemawiają za Actix Web. ## Architektura runtime: pinned-thread kontra work-stealing Fundamentalna różnica między Actix Web a Axum leży w sposobie zarządzania wątkami i planowania zadań asynchronicznych. Actix Web stosuje model pinned-thread: tworzy N izolowanych, jednowątkowych instancji runtime [Tokio](https://tokio.rs/), gdzie N odpowiada liczbie fizycznych rdzeni procesora. Każde przychodzące połączenie jest obsługiwane przez dokładnie jeden wątek przez cały cykl życia żądania. Dane nie migrują między wątkami, co eliminuje narzut synchronizacji i rywalizację o linie pamięci podręcznej (cache-line contention). Ten model sprawdza się szczególnie dobrze przy obciążeniach o jednorodnym charakterze, gdzie każde żądanie wymaga zbliżonej ilości pracy. Axum opiera się na jednym współdzielonym, wielowątkowym runtime Tokio z algorytmem work-stealing. Tokio dynamicznie przenosi zadania między wątkami w celu równomiernego rozłożenia obciążenia. Framework został zaprojektowany przez zespół Tokio jako referencyjny przykład wykorzystania ich runtime — każdy handler to zwykła funkcja async, a warstwa middleware bazuje na traicie `Service` z biblioteki [Tower](https://github.com/tower-rs/tower). Poniższe dwa przykłady ilustrują minimalny serwer HTTP w każdym z frameworków. ```rust // actix_hello.rs - Actix Web minimal server use actix_web::{web, App, HttpServer, HttpResponse}; async fn health() -> HttpResponse { HttpResponse::Ok().json(serde_json::json!({ "status": "ok" })) } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .route("/health", web::get().to(health)) }) .bind("0.0.0.0:8080")? .run() .await } ``` ```rust // axum_hello.rs - Axum minimal server use axum::{Router, Json, routing::get}; use serde_json::{json, Value}; async fn health() -> Json { Json(json!({ "status": "ok" })) } #[tokio::main] async fn main() { let app = Router::new() .route("/health", get(health)); let listener = tokio::net::TcpListener::bind("0.0.0.0:8080") .await .unwrap(); axum::serve(listener, app).await.unwrap(); } ``` Nawet na poziomie minimalnych przykładów widoczne są różnice projektowe. Actix Web wykorzystuje dedykowane makro `#[actix_web::main]` i wymaga jawnego zwracania `std::io::Result`. Axum korzysta ze standardowego `#[tokio::main]`, a handler zwraca typowany ekstraktor `Json` zamiast ręcznie konstruowanego `HttpResponse`. Struktura `Router` w Axum pełni rolę centralnego rejestru tras, podczas gdy Actix Web buduje aplikację za pomocą `App::new()` z łańcuchowymi wywołaniami `.route()`. ## Porównanie wydajności: benchmarki Actix Web 4.13 vs Axum 0.8 Dane z [TechEmpower Round 22](https://www.techempower.com/benchmarks/) oraz niezależnych testów społecznościowych konsekwentnie wskazują ten sam wzorzec. | Metryka | Actix Web 4.13 | Axum 0.8.9 | |--------|---------------|-------------| | Serializacja JSON (req/s) | ~720 000 | ~640 000 | | Plaintext (req/s) | ~980 000 | ~870 000 | | Zapytanie DB (req/s) | ~180 000 | ~170 000 | | Zużycie pamięci (hello world) | ~8 MB | ~6 MB | | Latencja P99 (JSON) | 1,2 ms | 1,4 ms | Actix Web konsekwentnie utrzymuje przewagę 10-15% w zakresie surowej przepustowości. Axum odpowiada niższym zużyciem pamięci — konsekwencja współdzielonego runtime Tokio. Dla porównania: oba frameworki przewyższają typowy serwer Go (net/http) 2-3-krotnie, a Node.js 5-8-krotnie na identycznym sprzęcie. W praktyce ta różnica ma realne znaczenie wyłącznie w scenariuszach ekstremalnej przepustowości: serwowanie reklam programatycznych, potoki ingestii danych analitycznych w czasie rzeczywistym czy bramki handlu algorytmicznego. Dla standardowego REST API obsługującego kilkanaście tysięcy żądań na sekundę wąskim gardłem i tak pozostanie baza danych lub komunikacja z usługami zewnętrznymi, nie sam framework. ## Wzorce ekstraktorów i obsługa żądań System ekstraktorów decyduje o tym, jak framework parsuje przychodzące żądania HTTP i udostępnia ich komponenty w handlerach. Axum 0.8 wprowadził istotne ulepszenia w tym obszarze — usunięto makro `#[async_trait]` na rzecz natywnych async traits (stabilnych od Rust 1.75) i dodano `OptionalFromRequestParts` do eleganckiej obsługi opcjonalnych parametrów. ```rust // axum_extractors.rs - Axum 0.8 extractor pattern use axum::{ extract::{Path, Query, State, Json}, routing::get, Router, }; use serde::Deserialize; use std::sync::Arc; #[derive(Deserialize)] struct Pagination { page: Option, per_page: Option, } struct AppState { db_pool: sqlx::PgPool, } async fn get_user( State(state): State>, Path(user_id): Path, Query(pagination): Query, ) -> Json { let page = pagination.page.unwrap_or(1); Json(serde_json::json!({ "user_id": user_id, "page": page })) } ``` ```rust // actix_extractors.rs - Actix Web extractor pattern use actix_web::{web, HttpResponse}; use serde::Deserialize; #[derive(Deserialize)] struct Pagination { page: Option, per_page: Option, } struct AppState { db_pool: sqlx::PgPool, } async fn get_user( state: web::Data, path: web::Path, query: web::Query, ) -> HttpResponse { let user_id = path.into_inner(); let page = query.page.unwrap_or(1); HttpResponse::Ok().json(serde_json::json!({ "user_id": user_id, "page": page })) } ``` W Axum ekstrakcja odbywa się przez destrukturyzację krotek w sygnaturze funkcji: `State(state)`, `Path(user_id)`. W Actix Web parametry są opakowywane w typy generyczne (`web::Data`, `web::Path`, `web::Query`), a dostęp do wartości wewnętrznych wymaga wywołania `.into_inner()`. Oba podejścia zapewniają bezpieczeństwo typów w czasie kompilacji, jednak składnia Axum jest bliższa idiomatycznemu Rust i czyta się bardziej naturalnie. Warto zaznaczyć, że Axum 0.8 zmienił składnię parametrów ścieżek z `/:id` na `/{id}`, co jest zgodne ze standardem OpenAPI i eliminuje potencjalne niejednoznaczności. ## Middleware: ekosystem Tower kontra system Actix Web Architektura middleware stanowi prawdopodobnie najważniejszą praktyczną różnicę między tymi frameworkami. Axum w pełni integruje się z biblioteką Tower, korzystając z traitów `Service` i `Layer`. Oznacza to, że każdy middleware napisany dla ekosystemu Tower — ograniczniki szybkości, warstwy tracing, kompresja HTTP, mechanizmy uwierzytelniania — działa z Axum bez żadnych modyfikacji. Co więcej, ta kompozycyjność wykracza poza protokół HTTP: ten sam middleware może opakowywać usługi gRPC realizowane przez Tonic. ```rust // axum_middleware.rs - Tower middleware composition use axum::{ Router, middleware, routing::get, extract::Request, response::Response, }; use tower_http::{ cors::CorsLayer, compression::CompressionLayer, trace::TraceLayer, }; use std::time::Instant; async fn timing_middleware( request: Request, next: middleware::Next, ) -> Response { let start = Instant::now(); let response = next.run(request).await; let duration = start.elapsed(); tracing::info!("Request took {:?}", duration); response } fn build_router() -> Router { Router::new() .route("/api/data", get(|| async { "ok" })) .layer(middleware::from_fn(timing_middleware)) .layer(CompressionLayer::new()) .layer(CorsLayer::permissive()) .layer(TraceLayer::new_for_http()) } ``` Actix Web posiada własny, niezależny system middleware oparty na traitach `Transform` i `Service` (nie tych z Tower). Middleware napisany dla ekosystemu Tower nie jest bezpośrednio kompatybilny — wymaga napisania warstwy adaptacyjnej lub przepisania od podstaw. Dla zespołów pracujących już z ekosystemem Tower — na przykład łączących usługi HTTP z gRPC przez Tonic lub korzystających z Hyper jako klienta HTTP — kompatybilność middleware Axum stanowi istotną przewagę operacyjną. Dla zespołów budujących izolowaną usługę HTTP własny system middleware Actix Web oferuje porównywalną funkcjonalność, choć kosztem mniejszej przenoszalności między projektami. ## Integracja z bazą danych: SQLx jako wspólny mianownik [SQLx](https://github.com/launchbadge/sqlx) to asynchroniczny zestaw narzędzi SQL dla Rust, który waliduje zapytania w czasie kompilacji. Kluczowa cecha: warstwa bazodanowa pozostaje identyczna niezależnie od tego, czy aplikacja korzysta z Actix Web, czy z Axum. ```rust // shared_db.rs - SQLx with compile-time query validation use sqlx::PgPool; #[derive(sqlx::FromRow, serde::Serialize)] struct User { id: i64, email: String, created_at: chrono::NaiveDateTime, } async fn find_user_by_email( pool: &PgPool, email: &str, ) -> Result, sqlx::Error> { sqlx::query_as!( User, "SELECT id, email, created_at FROM users WHERE email = $1", email ) .fetch_optional(pool) .await } ``` Makro `query_as!` łączy się z działającą instancją PostgreSQL w trakcie kompilacji i weryfikuje nazwy kolumn, typy danych oraz samo istnienie tabel. Błąd w nazwie kolumny powoduje błąd kompilacji, a nie awarię w środowisku produkcyjnym. Kompromisem jest konieczność dostępu do bazy danych w procesie budowania — w pipeline'ach CI rozwiązaniem jest polecenie `sqlx prepare`, które generuje metadane zapytań offline i zapisuje je w katalogu `.sqlx` commitowanym do repozytorium. ## Obsługa błędów: IntoResponse kontra ResponseError Wzorce obsługi błędów ujawniają odmienne podejścia projektowe obu frameworków. Axum opiera się na traicie `IntoResponse` w połączeniu ze standardowym typem `Result`. Actix Web korzysta z dedykowanego traitu `ResponseError`. ```rust // axum_errors.rs - Axum error handling with IntoResponse use axum::{ http::StatusCode, response::{IntoResponse, Response}, Json, }; enum AppError { NotFound(String), DatabaseError(sqlx::Error), ValidationError(String), } impl IntoResponse for AppError { fn into_response(self) -> Response { let (status, message) = match self { AppError::NotFound(msg) => ( StatusCode::NOT_FOUND, msg ), AppError::DatabaseError(_) => ( StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string(), ), AppError::ValidationError(msg) => ( StatusCode::BAD_REQUEST, msg ), }; (status, Json(serde_json::json!({ "error": message }))) .into_response() } } async fn get_user( axum::extract::Path(id): axum::extract::Path, ) -> Result, AppError> { if id <= 0 { return Err(AppError::ValidationError( "ID must be positive".to_string() )); } Ok(Json(serde_json::json!({ "id": id }))) } ``` Podejście Axum pozwala na naturalne wykorzystanie operatora `?` i implementacji traitu `From` do automatycznej konwersji błędów. Actix Web wymaga implementacji zarówno `Display`, jak i `ResponseError` dla każdego typu błędu. Oba wzorce prowadzą do bezpiecznego typowo API, jednak idiom Axum jest bliższy standardowym konwencjom Rust dotyczącym propagacji błędów. > **Wskazówka praktyczna** > > W obu frameworkach warto zdefiniować centralny typ `AppError` z implementacją `From`, `From` i innymi konwersjami. Pozwala to na stosowanie operatora `?` w całym kodzie handlerów bez jawnego mapowania błędów na każdym poziomie wywołań. ## Kiedy wybrać Actix Web Actix Web pozostaje najlepszym wyborem w następujących scenariuszach: - **Ekstremalnie wysoka przepustowość**: systemy serwowania reklam programatycznych, potoki ingestii danych analitycznych, bramki handlu algorytmicznego — wszędzie tam, gdzie 10-15% dodatkowych req/s przekłada się na mierzalną wartość biznesową. - **Aplikacje oparte na WebSocket**: wsparcie WebSocket w Actix Web jest dojrzalsze i sprawdzone w większej liczbie wdrożeń produkcyjnych niż odpowiednik w Axum. - **Istniejące projekty Actix Web**: migracja z Actix Web 3.x do 4.x przebiega płynnie. Przepisywanie stabilnej usługi produkcyjnej na Axum przynosi malejące korzyści przy rosnącym ryzyku regresji. - **Zespoły z doświadczeniem w Actix Web**: zmiana frameworka wyłącznie dla lepszej ergonomii rzadko uzasadnia koszt wdrożenia nowego narzędzia i przekwalifikowania zespołu. ## Kiedy wybrać Axum Axum jest lepszym dopasowaniem w tych kontekstach: - **Nowe projekty w 2026**: kompatybilność z [ekosystemem Tokio](https://tokio.rs/) (Tonic, Hyper, Tower) minimalizuje tarcia integracyjne i redukuje liczbę zależności. - **Usługi łączące HTTP i gRPC**: middleware Tower działa w obu protokołach, eliminując potrzebę pisania osobnych warstw dla każdego z nich. - **Zespoły wchodzące w ekosystem Rust**: typowane ekstraktory, czytelne komunikaty błędów kompilacji i składnia `/{id}` (zgodna z OpenAPI) oferują łagodniejszą krzywą nauki. - **Architektury mikroserwisowe**: trait `Service` z Tower umożliwia współdzielenie middleware (uwierzytelnianie, tracing, rate limiting) między wieloma usługami bez duplikacji kodu. ## Pytania rekrutacyjne: Actix Web i Axum w kontekście rozmów technicznych Rozmowy kwalifikacyjne na stanowiska backendowe w [Rust](/technologies/rust) coraz częściej obejmują pytania dotyczące frameworków webowych. Poniższe zagadnienia pojawiają się regularnie na rozmowach na poziomie senior i lead. **P: W jaki sposób model runtime Actix Web różni się od modelu Axum i jakie ma to konsekwencje wydajnościowe?** Actix Web tworzy osobny jednowątkowy runtime Tokio dla każdego rdzenia CPU. Zadania są przypięte do wątków i nie migrują, co eliminuje narzut work-stealingu i rywalizację o linie cache. Axum korzysta z jednego wielowątkowego runtime Tokio z work-stealingiem, który dynamicznie równoważy obciążenie. Model Actix Web generuje wyższą przepustowość pod ciągłym, jednorodnym obciążeniem. Model Axum lepiej radzi sobie z nierównomiernym obciążeniem dzięki dynamicznej redystrybucji zadań. **P: Jakie praktyczne znaczenie ma usunięcie `#[async_trait]` w Axum 0.8?** Axum 0.8 wykorzystuje natywne return-position impl Trait w traitach, ustabilizowane w Rust 1.75. Niestandardowe ekstraktory implementujące `FromRequestParts` lub `FromRequest` definiują metody async bez pośrednictwa makra `#[async_trait]`. Eliminuje to dynamiczną dyspozycję przez `Box`, co redukuje alokacje na stercie i skraca czas kompilacji. **P: Czym różni się middleware Tower od systemu middleware w Actix Web?** Tower definiuje generyczny trait `Service` niezależny od protokołu transportowego. Warstwa timeout lub rate limiting napisana dla Tower działa zarówno z Axum (HTTP), jak i z Tonic (gRPC). Actix Web stosuje własne traity `Transform` i `Service`, niezgodne z ekosystemem Tower. Praktyczna konsekwencja: middleware Axum jest wielokrotnie używalny w całym stosie opartym na Tokio, middleware Actix Web jest ograniczony do frameworka. **P: W jaki sposób SQLx waliduje zapytania SQL w czasie kompilacji i jakie są z tego wynikające kompromisy?** Makro `query_as!` z SQLx łączy się z działającą bazą danych PostgreSQL podczas kompilacji, weryfikując składnię SQL, nazwy kolumn, typy danych i istnienie tabel. Błąd w zapytaniu powoduje błąd kompilacji. Kompromis: proces budowania wymaga dostępu do bazy danych. Rozwiązaniem dla CI jest polecenie `sqlx prepare`, generujące offline metadane zapytań zapisywane w katalogu `.sqlx` w kontroli wersji. **P: W jakim scenariuszu wybór Actix Web zamiast Axum byłby technicznie uzasadniony?** Actix Web jest uzasadniony, gdy ciągła przepustowość stanowi główne ograniczenie architektoniczne — serwowanie reklam, ingestia danych analitycznych lub systemy handlu algorytmicznego. Model przypiętych wątków eliminuje narzut work-stealingu, dając 10-15% więcej req/s. Axum jest uzasadniony, gdy kompozycyjność z ekosystemem Tower i Tokio ma wyższą wartość niż marginalne różnice w przepustowości, zwłaszcza w architekturach mikroserwisowych łączących HTTP z gRPC. > **Błąd często popełniany na rozmowach** > > Kandydaci nierzadko próbują wskazać jeden framework jako obiektywnie lepszy. Mocne odpowiedzi bazują na analizie kompromisów: Actix Web optymalizuje przepustowość na poziomie pojedynczej usługi, Axum optymalizuje kompozycyjność i spójność ekosystemu. Właściwy wybór zależy od ograniczeń konkretnego systemu, nie od osobistych preferencji programisty. ## Podsumowanie - Actix Web 4.13 zapewnia 10-15% wyższą przepustowość dzięki modelowi runtime z przypiętymi wątkami — sprawdza się w usługach o ekstremalnych wymaganiach wydajnościowych - Axum 0.8 oferuje lepszą ergonomię dzięki natywnym async traits, składni `/{id}` zgodnej z OpenAPI i pełnej kompatybilności z middleware Tower - Warstwa bazodanowa (SQLx z walidacją w czasie kompilacji) jest identyczna dla obu frameworków — wybór frameworka nie determinuje wzorców dostępu do danych - Axum zużywa mniej pamięci (~6 MB vs ~8 MB w scenariuszu hello world) dzięki współdzielonemu runtime Tokio - Dla nowych projektów webowych w Rust bez ekstremalnych wymagań przepustowości Axum redukuje długoterminowy koszt utrzymania poprzez spójność z ekosystemem Tokio, Tonic i Tower - Pytania rekrutacyjne dotyczące tego tematu weryfikują zdolność kandydata do rozumowania o kompromisach architektonicznych, a nie deklaratywną znajomość API - [Przygotowanie do rozmów kwalifikacyjnych z Rust](/blog/rust/rust-interview-questions) wymaga zrozumienia decyzji projektowych obu frameworków i umiejętności argumentowania wyboru w kontekście konkretnych ograniczeń systemu --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/rust/rust-actix-web-vs-axum-comparison