Understanding Redux Middleware And Writing Custom Ones
Tags: redux, react, javascript
Published: 2017-09-06 16:26:53

---
title: Understanding Redux Middleware And Writing Custom Ones
published: true
description: In this article, I explain what are Redux middleware, what are some use-cases where middleware are important, and how to write your own custom middleware.
tags: redux, react, javascript
canonical_url: https://designingforscale.com/understanding-redux-middleware-and-writing-custom-ones/
---

If you use Redux, you’ve most likely used redux middleware before through - for example - `redux-thunk`, `redux-promise-middleware`, `redux-saga` or `redux-logger`. These are important middleware in most apps but yet some of us (including me) take this concept for granted without digging any further.

With that said, I recently had to implement a caching solution in a large application and, after doing some research and poking one of my colleague’s brain (thank you Rec!), decided that implementing a custom middleware was the best approach for this, mainly because:

* It makes for cleaner code
* It makes for more maintainable code (think separation of concerns)
* It groups all the caching logic in one location

In this article, I’d like to explain what a Redux middleware is, and how I implemented a custom middleware.

### What is a Middleware?
For backend developers, a Redux middleware is similar to a middleware in Express or in ASP.NET. Although it is not exactly the same thing, it’s similar and represents a good way of thinking of this concept.

In Redux, a middleware is used to intercept dispatched actions before they make it to the reducer. This means that when you call `dispatch` on an action, the action goes through a (or many) middleware before hitting the reducer - if it even makes it that far, but more on that later.

![This is what the Redux middleware flow looks like](https://designingforscale.com/content/images/2017/09/reduxMiddleware.png)

You can apply multiple middleware to a Redux store, which means that the action will have to go through all the middleware before making it to the reducer. The order of execution is actually the order in which you pass the middleware to the store. Also, at any point in a middleware, you can chose to stop forwarding the action, which will end the cycle.

For instance, in my caching middleware, I first check if the _same action_ is already in progress. If it is, I cache the latest payload and interrupt the flow by returning out of the middleware. Since I’m not calling `next` or `dispatch`, the action flow will not continue.

### Why Use a Middleware?
As expressed above, actions go through middleware before getting to the reducers, which gives us a great way of applying logic or filters to all actions. This means that the logic is grouped in one place instead of being spread across reducers, that we can easily identify where to investigate if a bug occurs, and we can swap that code out if we ever need to.

Some use-cases that benefit from using middleware:

* Logging: every action goes through this middleware, so we can log its type and payload for debugging or tracking purposes.
* Error tracking: if any asynchronous action returned an error, this middleware can display a notification.
* Caching: Only call your API for the same action once, and cache the result for future calls.
* Auth requests: For API calls, apply an authentication token before sending out the request.
* So much more :)

### Writing a Middleware
To define your own middleware, you need to write a function with the following signature:
`store => next => action => result`

This looks very confusing at first glance - I hear you - so let’s break it down a little bit:

* `store` is the Redux store instance that will be passed to your middleware.
* `next` is a function that you need to call with an action when you want to continue the flow execution, which means passing the action to the next in line: either the following middleware or a reducer.
* `action` is the action that was originally dispatched so that you can access it, apply logic based on the action, and eventually pass it on using `next`.
* `result` is the value used as the result of the dispatch call.

Finally, to apply this middleware to the Redux store, you need to call `applyMiddleware` when creating the store through `createStore()`. Here’s a nice example from the official Redux documentation:

```
import { createStore, combineReducers, applyMiddleware } from 'redux'

let todoApp = combineReducers(reducers)
let store = createStore(
  todoApp,
  // applyMiddleware() tells createStore() how to handle middleware
  applyMiddleware(logger, crashReporter)
)
```

In the example above, the middleware `logger` will be called first, followed by the `crashReporter` middleware since this is the order in which they were passed to `applyMiddleware`.

### The Caching Middleware
As mentioned in this article’s outline, I implemented a caching middleware recently to solve a very specific issue. I know that there are existing caching middleware out there, but I needed something small and specific to the issue at hand, so I wrote a few lines of code instead of using an existing library.

For this issue, I had to make sure that a WYSIWYG editor only called the backend sequentially when saving the content. For instance, if auto-save kicked in while a save was already occurring, I did not want to send the text to the backend until the previous call completed. The same concept also applies if the user hit the **Save** button multiple times.

Here’s what my middleware looks like:

```javascript
export default function textUpdatesMiddleware () {
  return store => next => action => {
    if (action.type === UPDATE_TEXT) {
  	  // Check if the new text in the payload is different from what we already have in the store
      if (!shouldSaveText(action.payload, store)) return

	  // Are we currently saving?
	  // isUpdatingText is set to `true` in a reducer
	  // A reducer listens to CACHE_TEXT_UPDATE and will store the payload into `pendingTextUpdate`
	  // We only cache the latest content, not all of them
      if (store.getState().isUpdatingText) {
        return store.dispatch({
          type: CACHE_TEXT_UPDATE,
          payload: action.payload
        })
      } else {
  	    // This uses `redux-promise-middleware`
        return store.dispatch({
  		  type: UPDATE_TEXT,
		  payload: {
      	    promise: http.patch(apiEndpoint, content)
    	  }
  	    })
      }
    }
	// This uses the `redux-promise-middleware` convention of _PENDING, _FULFILLED, _REJECTED
    if (action.type === UPDATE_TEXT_FULFILLED) {
      const pendingTextUpdate = store.getState().pendingTextUpdate
      // If we had a pending update
      if (pendingTextUpdate) {
		// A reducer listens to UNCACHE_TEXT_UPDATE and will clear `pendingTextUpdate`
        store.dispatch({ type: UNCACHE_TEXT_UPDATE })
		// Allow the fulfilled action to continue on to the reducers
        next(action)
        // Dispatch the update with the cached content
		return store.dispatch({
          type: UPDATE_TEXT,
          payload: pendingTextUpdate
        })
      }
    }
    // Nothing to do here - keep calm and carry on
	next(action)
  }
}
```

Based on the code above, it’s worth noting that Redux applies some magic when you call `store.dispatch` from within a middleware and the action will travel through all the middleware again, including the current middleware that dispatched it. However, when you call `next`, the action moves on to the next middleware in the flow.

### Conclusion
This middleware solves a specific issue I was experiencing, but we could just as well make it more generic so that it applies the same concept to all (or a subset of) actions. At this point, I don’t have any need to make it generic so I did not want to over-engineer it, but it’s worth noting that it’s definitely doable.

If I hadn’t applied this logic in a middleware, I would have had to validate that an API call is not currently in-progress from a reducer, then dispatch calls from the reducer to cache the content, and also listen for the `FULFILLED` action from the reducer or the `then` on the http call, and then re-dispatch the action. This gets messy real quick and doesn’t scale well if we need to make it more generic.

I hope this was a good introduction to middleware and that it covered enough of the basics to get you started if you ever need to write your own custom one.

My final piece of advice is that research and discussions are very valuable. I am very glad I decided to not go with my original (bad) approach because something felt wrong and that I did more research, discussed it with a colleague, and ended up settling on using a middleware because the final result is a better solution.

### Credit
* The official Redux documentation section on [Middleware]([Middleware · Redux](http://redux.js.org/docs/advanced/Middleware.html)).
* [You Aren’t Using Redux Middleware Enough]([You Aren’t Using Redux Middleware Enough – Jacob Parker – Medium](https://medium.com/@jacobp100/you-arent-using-redux-middleware-enough-94ffe991e6)).

*This post was originally published on [Designing for Scale](https://designingforscale.com/?utm_source=redux-middleware&utm_medium=guest-post&utm_campaign=devto)*