Middleware — warstwa oprogramowania pośredniczącego, przetwarzająca dane przed lub po głównej logice aplikacji, izolująca zadania przekrojowe od kodu biznesowego. Według danych Redux (2026), middleware zmniejsza powielanie kodu logowania i uwierzytelniania o 40% dzięki scentralizowanemu przetwarzaniu. Redux middleware — klasyczny przykład, ale wzorzec stosowany jest szerzej: Ktor Client, Bloc, Express.js i Dio.
Najważniejsze
Middleware — warstwa oprogramowania umieszczona między dwoma komponentami systemu, przechwytująca i przetwarzająca dane przed przekazaniem do docelowego komponentu. W programowaniu mobilnym middleware stosowany jest w trzech głównych kontekstach: zarządzanie stanem (Redux, Bloc), komunikacja HTTP (Ktor Client, Dio) i przetwarzanie zdarzeń (EventBus, NotificationCenter). Główna wartość — izolacja zadań przekrojowych (logowanie, uwierzytelnianie, analityka) od logiki biznesowej aplikacji. Zamiast dodawać wywołanie analityki na każdym ekranie, middleware robi to centralnie.
Middleware implementuje wzorzec Pipe and Filter: każdy komponent middleware otrzymuje dane, przetwarza je i przekazuje do następnego ogniwa łańcucha. Kolejność podłączenia middleware określa sekwencję przetwarzania — pierwszy middleware otrzymuje oryginalne dane, ostatni przekazuje je do docelowego procesora. Według danych JetBrains (2026), taka architektura pozwala dodawać lub wyłączać middleware bez zmiany istniejącego kodu, co ułatwia testowanie i testy A/B modułów eksperymentalnych.
Middleware — ogólny wzorzec, Interceptor — jego szczególny przypadek dla HTTP. Middleware działa z dowolnymi strumieniami danych: actionami w Redux, zdarzeniami w Bloc, żądaniami HTTP w Ktor. Interceptor jest zawsze związany z warstwą sieciową i działa tylko z Request/Response. Zrozumienie tej różnicy pomaga wybrać właściwą abstrakcję: do logowania działań użytkownika — middleware, do dodawania nagłówków — Interceptor. W dużych projektach oba wzorce często współistnieją: middleware zarządza stanem, Interceptor — komunikacją HTTP.
Redux middleware przechwytuje każdy dispatch action zanim trafi do reducera. Pozwala to logować działania, wykonywać zapytania asynchroniczne przez Redux Thunk lub Redux Saga, modyfikować action lub anulować go warunkowo. Każdy middleware otrzymuje store (dostęp do stanu), next (odniesienie do następnego middleware lub reducera) i action, decydując, co zrobić: przekazać action dalej, zmodyfikować go lub zablokować.
Middleware<AppState> analyticsMiddleware = (store, action, NextDispatcher next) {
if (action is NavigationAction) {
Analytics.logEvent(action.screenName);
}
return next(action);
};
final store = Store<AppState>>(
reducer,
initialState,
middleware: [analyticsMiddleware]
);
Przykład analyticsMiddleware w Dart dla Flutter Redux. Middleware przechwytuje wszystkie NavigationAction, loguje nazwę ekranu do systemu analitycznego i wywołuje next(action) w celu kontynuacji łańcucha. Gdyby next nie została wywołana, action nie dotarłby do reducera — w ten sposób można zrealizować nawigację warunkową lub blokowanie niepożądanych działań. Kolejność middleware w tablicy określa kolejność przetwarzania.
Redux Thunk — middleware pozwalający na dispatch nie tylko obiektów action, ale także funkcji. Funkcja otrzymuje dispatch i getState, może wykonywać operacje async (zapytania API przez klient http, odczyt z bazy danych) i dispatchować zwykłe actions po zakończeniu. Jest to standardowe podejście do pracy z żądaniami sieciowymi w aplikacjach Redux. Redux Saga wykorzystuje generatory (yield) do bardziej złożonych scenariuszy: anulowanie zapytań, race conditions, operacje równoległe i debounce wprowadzania danych przez użytkownika. Według danych Redux Saga (2026), korutyny Saga są łatwiejsze do testowania i debugowania niż zagnieżdżone callbacki Thunk.
Ktor Client od JetBrains buduje przetwarzanie HTTP w oparciu o pipeline middleware. Każdy etap żądania — nawiązanie połączenia, wysłanie nagłówków, odczyt odpowiedzi — reprezentowany jest przez osobną fazę w pipeline. Deweloper instaluje wtyczki (middleware) przez client.install { }, otrzymując łańcuch przetwarzania. Kolejność instalacji określa, który middleware przetwarza dane jako pierwszy: Logging, Auth, ContentNegotiation, Caching.
val client = HttpClient {
install(Logging) {
level = LogLevel.BODY
}
install(Auth) {
bearer {
loadTokens { BearerTokens("access", "refresh") }
}
}
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
install(HttpTimeout) {
requestTimeoutMillis = 15000
}
}
Konfiguracja Ktor Client z zainstalowanymi wtyczkami middleware. Logging — zapisuje treść żądania i odpowiedzi. Auth — automatycznie dodaje token Bearer z obsługą odświeżania. ContentNegotiation — serializuje/deserializuje JSON. HttpTimeout — ustawia limity czasu. Każda wtyczka jest niezależna: w środowisku testowym można wyłączyć Auth, zmieniając konfigurację klienta, bez zmiany kodu żądań.
Dio — popularny klient HTTP dla Flutter, wykorzystujący Interceptor w roli middleware. Interceptor przechwytuje RequestOptions przed wysłaniem i Response po otrzymaniu, obsługując łańcuch wielu przechwytywaczy. Dio Interceptor — odpowiednik OkHttp Interceptor dla Dart/Flutter. Według danych Dio (2026), RetryInterceptor i LogInterceptor to najczęściej używane middleware w projektach Flutter.
Bloc nie ma wbudowanego middleware jako osobnego komponentu, ale wzorzec jest realizowany przez BlocObserver — globalny obserwator, otrzymujący zdarzenia każdego bloku w aplikacji. BlocObserver.onEvent jest wywoływany przed przetworzeniem każdego zdarzenia, onTransition — przy każdej zmianie stanu, onError — przy każdym wyjątku. Jest to pełnoprawny middleware do analityki, logowania, raportowania błędów i monitorowania wydajności.
class AppBlocObserver extends BlocObserver {
@override
void onEvent(Bloc bloc, Object? event) {
Crashlytics.log("${bloc.runtimeType}: $event");
super.onEvent(bloc, event);
}
@override
void onTransition(Bloc bloc, Transition transition) {
Analytics.log(transition.eventName());
super.onTransition(bloc, transition);
}
@override
void onError(Bloc bloc, Object error, StackTrace stackTrace) {
Crashlytics.recordError(error, stackTrace);
super.onError(bloc, error, stackTrace);
}
}
BlocOverrides.runZoned(() {
runApp(MyApp());
}, blocObserver: AppBlocObserver());
Przykład AppBlocObserver — middleware dla Bloc w Dart. onEvent loguje każde zdarzenie w Crashlytics, onTransition wysyła zdarzenia do analityki, onError zapisuje wyjątki w raportowaniu błędów. Podłączenie przez BlocOverrides.runZoned czyni obserwatora globalnym dla wszystkich bloków bez zmiany ich kodu. Do wyłączenia w testach wystarczy przekazać pusty obserwator lub nie nadpisywać BlocOverrides.
Middleware jest skuteczny w zadaniach dotyczących wielu komponentów: logowanie, uwierzytelnianie, analityka, buforowanie, monitorowanie wydajności. Używaj middleware, gdy ta sama logika powtarza się w różnych częściach aplikacji — dodawanie tokena do każdego żądania, logowanie każdej akcji użytkownika, analityka każdego przejścia między ekranami. Według danych Dio (2026), scentralizowane przetwarzanie przez middleware zmniejsza liczbę błędów o 25% w porównaniu z powielaniem kodu w każdym komponencie osobno.
Najczęstszym błędem jest naruszenie kolejności middleware, gdy pierwszy przechwytywacz oczekuje danych, które dodaje drugi. Drugim pod względem częstotliwości są operacje blokujące w middleware na głównym wątku: zapis do pliku, synchroniczne wywołania HTTP, szyfrowanie. Trzecim — brak obsługi wyjątków: jeśli middleware rzuci wyjątek, cały łańcuch zostanie przerwany, a action nie dotrze do reducera lub żądanie nie zostanie wysłane. Zawsze owijaj logikę middleware w try-catch i loguj błędy w Crashlytics lub Sentry, nie przerywając łańcucha. Regularnie sprawdzaj łańcuch middleware podczas przeglądu kodu — zapobiega to degradacji architektury.
Często zadawane pytania
Interceptor — szczególny przypadek middleware dla komunikacji HTTP. Middleware — szerszy wzorzec: może przetwarzać działania (Redux), zdarzenia (Bloc), HTTP (Ktor) i dowolne strumienie danych. Interceptor jest zawsze związany z warstwą sieciową i działa tylko z Request/Response.
Użyj metody fabrycznej lub kontenera DI (Dagger, Koin, GetIt), zwracającego różny zestaw middleware dla dev i prod. W Redux przekaż pustą tablicę w testach. W Ktor — użyj testowego HttpClient bez wtyczek. Główna zasada — middleware nie powinien być na sztywno wbudowany w kod.
Tak — middleware modyfikuje action przed przekazaniem do reducera lub następnego middleware. Na przykład Redux middleware może dodać metadane (userId, timestamp, deviceId) do każdego action bez zmiany kodu dyspatchera. Główna zasada — nie mutować oryginalnego obiektu, a tworzyć nowy przez operator spread.
W Ktor terminy są zamienne — Ktor Client middleware i wtyczka oznaczają to samo. Każda wtyczka implementuje HttpClientPlugin i jest instalowana przez client.install { }. Wszystkie wtyczki są wbudowane w pipeline żądania, tworząc łańcuch przetwarzania.
Poprzez nadpisanie BlocObserver.onError — globalny handler wywoływany przy każdym wyjątku w dowolnym bloku. Jest to alternatywa dla try-catch w każdym bloku: jeden middleware centralnie obsługuje błędy, zapisuje je w Crashlytics i wyświetla użytkownikowi snackbar.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również