How Redux puts middleware together

Middleware software can mean different things depending on different platforms, but in general terms it can allow an application to communicate to an external application to utilize services that may not be initially available. In some implementations, middleware works by intercepting data that comes from an external source and utilizing it to enhance a part of the system. Such functionality can vary from performing data validation, filtering, monitoring, reporting, talking to an external API, and more.

Middleware in Redux?

Similar to other libraries like node’s express.js, Redux extends its core functionality by letting us create middleware that can do virtually anything we want to, but in Redux’s case, the concept is applied in the context of its popular unidirectional flow cycle (actions, reducers, store).

This specific implementation works by intercepting the data that flows between dispatching an action and the moment it gets to the reducer. This is a good place for middleware to live because it gets access to data right before it gets stored in the state, making it ideal for solving problems where you need to enhance action creators with things like promises, thunks (delayed actions), or intercepting data to analyze it for analytics, reporting, catching errors, etc.


The store starts it all

Besides holding the main state of your application, the ‘store’ owns a ‘dispatch’ method that is capable of updating the state itself. A ‘Dispatch’ method fires actions that contain payloads of data that when passed to the reducers, will ultimately emit state changes. When we create a store using middleware, we’re basically creating an enhanced dispatch method that has knowledge of all the middleware functions, so every time we dispatch an action, data passes through each one of them.

When creating the store, applyMiddleware() is the key function that will tell createStore() how to handle middleware. It creates a new dispatch for the store by using the base dispatch and feeding it to a chain of middleware.


applyMiddleware() at a glance

The order in which we define our middleware matters since the result of each one will be passed to the next one down the list. The job of applyMiddleware() goes a little something like the following.

Say we start with an Array of the middleware functions m1, m2, m3.


Each function is then given a store.getState() & store.dispatch().


To create a composition of the provided functions where each one of them receives the output of the one that follows it to create a new dispatch.


To understand this part better it’s useful to look at how the middleware function looks.

The middleware function

In order to operate in conjunction, middleware functions must receive the necessary methods to access the current state and to be able to dispatch actions. If we look at the basic anatomy of a middleware function we’ll see that they receive an ‘action’ object, then the ‘dispatch’ method which is called ‘next’, and the ‘store’ which would be used to obtain the current state of our application.

Each middleware will return the result of the computed action as a dispatch (passed as ‘next’), so the next middleware down the chain can use this dispatch to execute the next action.


As you might have noticed, there’s a fair amount of functional programming concepts included in this process. For example if you look at the source code you’ll see that the custom function compose utilizes ‘reduce’ to accumulate functions from right to left so when we write compose(f, g, h), the result is the same as f(g(h(x))).


Source code
Middleware (Docs)
The Store (Docs)