Middleware es una capa de software intermedia que procesa datos antes o después de la lógica principal de la aplicación, aislando las preocupaciones transversales del código de negocio. Según Redux (2026), el middleware reduce la duplicación de código para registro y autenticación en un 40% mediante el procesamiento centralizado. Redux middleware es un ejemplo clásico, pero el patrón se usa más ampliamente: Ktor Client, Bloc, Express.js y Dio.
Puntos clave
Middleware es una capa de software ubicada entre dos componentes del sistema, que intercepta y procesa datos antes de pasarlos al componente destino. En el desarrollo móvil, el middleware se usa en tres contextos principales: gestión de estado (Redux, Bloc), comunicaciones HTTP (Ktor Client, Dio) y manejo de eventos (EventBus, NotificationCenter). El valor fundamental es el aislamiento de preocupaciones transversales (registro, autenticación, analítica) de la lógica de dominio de la aplicación. En lugar de añadir llamadas de analítica a cada pantalla, el middleware lo hace de forma centralizada.
El middleware implementa el patrón Pipe and Filter: cada componente middleware recibe datos, los procesa y los pasa al siguiente eslabón de la cadena. El orden de conexión del middleware determina la secuencia de procesamiento — el primer middleware recibe datos sin procesar, el último los pasa al manejador destino. Según JetBrains (2026), esta arquitectura permite añadir o eliminar middleware sin cambiar el código existente, simplificando las pruebas y el testing A/B de módulos experimentales.
Middleware es un patrón general, Interceptor es su caso especial para HTTP. Middleware funciona con cualquier flujo de datos: acciones en Redux, eventos en Bloc, peticiones HTTP en Ktor. Interceptor siempre está vinculado a la capa de red y solo trabaja con Request/Response. Entender esta diferencia ayuda a elegir la abstracción correcta: para registrar acciones del usuario — middleware, para añadir cabeceras — Interceptor. En proyectos grandes, ambos patrones suelen coexistir: middleware gestiona el estado, Interceptor maneja las comunicaciones HTTP.
Redux middleware intercepta cada acción dispatch antes de que llegue al reducer. Esto permite registrar acciones, realizar peticiones asíncronas mediante Redux Thunk o Redux Saga, modificar una acción o cancelarla condicionalmente. Cada middleware recibe store (acceso al estado), next (referencia al siguiente middleware o reducer) y action, decidiendo qué hacer: pasar la acción, modificarla o bloquearla.
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]
);
Ejemplo de analyticsMiddleware en Dart para Flutter Redux. El middleware intercepta todos los NavigationAction, registra el nombre de la pantalla en el sistema de analítica y llama a next(action) para continuar la cadena. Si no se llamara a next, la acción no llegaría al reducer — así se puede implementar navegación condicional o bloquear acciones no deseadas. El orden del middleware en el array determina la secuencia de procesamiento.
Redux Thunk es un middleware que permite despachar no solo objetos de acción sino también funciones. La función recibe dispatch y getState, puede realizar operaciones asíncronas (peticiones API mediante cliente HTTP, lectura de BD) y despachar acciones normales al finalizar. Este es el enfoque estándar para peticiones de red en aplicaciones Redux. Redux Saga usa generadores (yield) para escenarios más complejos: cancelación de peticiones, condiciones de carrera, operaciones paralelas y debounce de entrada del usuario. Según Redux Saga (2026), las corrutinas Saga son más fáciles de probar y depurar que los callbacks anidados de Thunk.
Ktor Client de JetBrains construye el procesamiento HTTP basado en pipeline middleware. Cada etapa de la petición — configuración de conexión, envío de cabeceras, lectura de respuesta — está representada por una fase separada en el pipeline. El desarrollador instala plugins (middleware) mediante client.install { }, obteniendo una cadena de procesamiento. El orden de instalación determina qué middleware procesa los datos primero: 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
}
}
Configuración de Ktor Client con plugins middleware instalados. Logging — escribe el cuerpo de la petición y respuesta. Auth — añade automáticamente el token Bearer con soporte de refresh. ContentNegotiation — serializa/deserializa JSON. HttpTimeout — establece tiempos de espera. Cada plugin es independiente: en un entorno de pruebas se puede deshabilitar Auth reemplazando la configuración del cliente sin cambiar el código de las peticiones.
Dio es un cliente HTTP popular para Flutter, que usa Interceptor como middleware. Interceptor intercepta RequestOptions antes de enviar y Response después de recibir, soportando una cadena de múltiples interceptores. Dio Interceptor es análogo a OkHttp Interceptor para Dart/Flutter. Según Dio (2026), RetryInterceptor y LogInterceptor son los middleware más usados en proyectos Flutter.
Bloc no tiene middleware integrado como componente separado, pero el patrón se implementa mediante BlocObserver — un observador global que recibe eventos de cada bloque en la aplicación. BlocObserver.onEvent se llama antes de procesar cada evento, onTransition en cada transición de estado, onError en cada excepción. Es un middleware completo para analítica, registro, reporte de fallos y monitoreo de rendimiento.
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());
Ejemplo de AppBlocObserver — middleware para Bloc en Dart. onEvent registra cada evento en Crashlytics, onTransition envía eventos a analítica, onError escribe excepciones en el reporte de fallos. La conexión mediante BlocOverrides.runZoned hace que el observer sea global para todos los bloques sin cambiar su código. Para deshabilitarlo en pruebas, basta con pasar un observer vacío o no sobrescribir BlocOverrides.
El middleware es efectivo para tareas que afectan a muchos componentes: registro, autenticación, analítica, caché, monitoreo de rendimiento. Usa middleware cuando la misma lógica se repite en diferentes partes de la aplicación — añadir un token a cada petición, registrar cada acción del usuario, analítica de cada transición entre pantallas. Según Dio (2026), el procesamiento centralizado mediante middleware reduce los errores en un 25% en comparación con duplicar código en cada componente por separado.
El error más frecuente es el orden incorrecto del middleware, cuando el primer interceptor espera datos que añade el segundo. El segundo error más común son operaciones bloqueantes en middleware en el hilo principal: escritura de archivos, llamadas HTTP síncronas, cifrado. El tercero es la falta de manejo de excepciones: si un middleware lanza una excepción, toda la cadena se rompe y la acción no llegará al reducer o la petición no se enviará. Siempre envuelve la lógica del middleware en try-catch y registra los errores en Crashlytics o Sentry sin romper la cadena. Revisa regularmente la cadena de middleware durante las revisiones de código — esto previene la degradación de la arquitectura.
Preguntas frecuentes
Interceptor es un caso especial de middleware para comunicaciones HTTP. Middleware es un patrón más amplio: puede manejar acciones (Redux), eventos (Bloc), HTTP (Ktor) y cualquier flujo de datos. Interceptor siempre está vinculado a la capa de red y solo funciona con Request/Response.
Usa un método fábrica o contenedor DI (Dagger, Koin, GetIt) que devuelva un conjunto diferente de middleware para dev y prod. En Redux, pasa un array vacío en las pruebas. En Ktor, usa un HttpClient de prueba sin plugins. El principio principal es que el middleware no debe estar hardcodeado.
Sí — el middleware modifica la acción antes de pasarla al reducer o al siguiente middleware. Por ejemplo, Redux middleware puede añadir metadatos (userId, timestamp, deviceId) a cada acción sin cambiar el código del despachador. La regla principal es no mutar el objeto original sino crear uno nuevo mediante el operador spread.
En Ktor, los términos son intercambiables — Ktor Client middleware y plugin significan lo mismo. Cada plugin implementa HttpClientPlugin y se instala mediante client.install { }. Todos los plugins se integran en el pipeline de la petición, formando una cadena de procesamiento.
Sobrescribiendo BlocObserver.onError — un manejador global llamado en cada excepción en cualquier bloque. Es una alternativa a try-catch en cada bloque: un middleware centralizado maneja errores, los escribe en Crashlytics y muestra un snackbar al usuario.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también