Connecting Redux to your API

Emil Ong
Haus Engineering Blog
9 min readApr 7, 2016

I started using Redux last summer and one of the first things I needed to do was connect it to a backend. The awesome Redux docs have an example of how to do this, but since they were written, alternative approaches have emerged and matured. This article is an attempt to survey the general approaches, give their benefits and drawbacks, and link to a few implementations, including some which are not strictly (or at all) Redux. I won’t pass judgment or suggest one approach, but I will give some examples from my experience writing a Redux/React app which might be helpful to folks in a similar situation.

Disclaimer! I wrote this in no small part as notes for myself on the current state of things in Redux and I wouldn’t be the least bit surprised if I have omitted a relevant library, example, or even entire approach and/or misstated something here. I would love your feedback on anything I missed or messed up! The Redux community is prolific and profound and I’m always happy to learn more. 💛💛💛

What’s in the Store?

One of the first things you may think of when connecting to your backend and/or APIs is that you want that data in Redux and it’ll just flow down into your React components for display. Then questions emerge about how you push that data into Redux and how you select it for your components.

As it turns out, there are a number of controversial and missing things from that notion alone. Welcome to modern web development. 😝

Metadata

Let’s start with something that is (hopefully) uncontroversially missing: request metadata. When you’re making asynchronous requests to an API, you almost certainly need to display something to the user not only about the actual data transmitted but about the request itself. If the request fails for any reason such as a backend failure, the user not being logged in, a timeout, or whatever, you’ll probably want to let the user know. Even if the request is a success, you’ll likely want some sort of indication to the user that something is happening. There are many cases where you’ll also want the user to know that something has happened, e.g. when the backend has accepted a POST, et al. Beyond UX, you’ll also want to avoid duplicate calls for the same data, or worse, with the same action, especially if it’s not idempotent.

As it turns out, most Redux examples make the implicit assumption that metadata is the first thing you want to keep in your store. This assumption is totally fair, but I thought it’s important to call out here explicitly. (My initial thought was that I wanted backend data in the store.)

Data

Next up, let’s talk about the data from the backend being in the store. As it turns out, there is not agreement on this idea. The fine folks at Heroku offer another option called React Refetch, which is to have a store explicitly for data from the backend, leaving Redux itself for client-side interaction state data only. The React Refetch model has a number of cool features that manage metadata and offer declarative, managed fetches of data from your backend. For the rest of this article, I’ll be talking about keeping data in Redux, but I’ll reference some of these other ideas from React Refetch, which are very cool, independent of where you store your data.

Another debatable topic on storing backend data in Redux is how to structure it. I’ll discuss this in the “Getting data out of the store” section below.

Getting data into the store

Let’s assume for the moment that you need to initiate some call to the backend to get data into your store. As we discussed above, you probably want to keep data about the call as well as the data received in the store. Who performs the call and what are the synchronization concerns?

There are at least a couple of approaches to dealing with these issues, broadly broken down into the imperative and declarative camps. Note that declarative in this context means something different than our discussion of React Refetch above in the sense of what we’re declaring. I’ll discuss this more later in the “There’s declarative and then there’s declarative” section.

Imperative approaches

The imperative approach is the one outlined in the Redux docs. Basically, you have an asynchronous task that fetches data from the backend. It knows when to update the data and metadata in the store and dispatches actions to do so at the appropriate times. The example in the Redux docs uses thunk middleware for kicking off the fetch from the dispatch method, but strictly speaking, this isn’t entirely necessary.

High-level imperative workflow

The above is a sketch of how a call in an imperative approach might work. The metadata and received data could be one dispatch or two, as shown above. One thing to note is how all the arrows from the fetcher code go out to the store and not back. The code below implements this pattern as the “Fetcher code” box above:

redux-rest, redux-requests, and redux-api are all libraries that help reduce the boilerplate and common patterns with this approach. If you’re using Firebase specifically, firedux is an imperative client that works with redux.

Declarative approaches

Declarative approaches to asynchronous tasks (of which fetching from APIs/backends) have emerged recently to solve some problems that are somewhat ill-served by the imperative approach. Namely, performing a sequence of asynchronous actions. Two libraries that I’m aware of providing this style are redux-saga and redux-loop, the former being the more popular of the two at the time of writing.

High-level declarative workflow

These are referred to as “declarative” because, to quote the redux-loop readme, “Effects are declarative specifications of the next behavior of the store.” Thus your reducers remain purely functional and all the non-pure functional behavior happens outside. Here’s some code for redux-saga that does roughly what the imperative example did:

The thingRequestSaga generator would be registered with the redux-saga middleware and observe each action, yielding the next effect until it’s completed its saga. redux-saga makes heavy use of ES6 Generators, which provides a nice way to lay out sequential, but asynchronous effects that are easily testable.

redux-loop has an interesting approach of putting such logic into reducers which can return a state that includes the next asynchronous effects to run. See the redux-loop readme for an example.

Imperative or declarative?

In general, it appears as if the community is trying to find the best way to move to the declarative approach. redux-saga and redux-loop are both relatively new and highly promising, but until you encounter the problem of trying to do multiple sequential (or parallel!) steps in your application, the imperative approach might seem more straightforward.

Looking back at the imperative entry point above, you can see that there’s a trigger in the application to kick off the fetch. If you need another fetch after this one, you need either to trigger that fetch from your application again after finishing this call or by putting that sequence into this fetch trigger. The latter approach may leave you with duplicated and/or tightly bound code if the sequencing is conditional or triggered from various different places in your code.

I’ll give an unfortunate example of the former whose provenance is all too close to home. 😉

Say you trigger a fetch of a list feed in one view and each list item has identifiers of other resources you could fetch to enhance the view progressively. In that view you may choose to trigger fetches of the subitems after receiving the list. In fact, you may choose to trigger it from the view because that’s where you triggered the first call. This approach may not seem that bad initially because the view knows what data it needs and knows how to display. The issue is that it now has to know where to get it from. In other words, this example has moving complex fetch coordination into the view, which is probably a tight coupling we don’t want.

In the declarative world, we could set up a sequence of fetch effects and compose them for different views. Note that the sequences are initiated via dispatched actions, which can come from the application itself or from another side-effect, so that composition becomes much more natural and encapsulated.

Getting data out of the store

Now that we have some techniques for getting data into the store, what about extracting it into views? Of course, React-Redux gives us the connect() wrapper to inject the data from the store into our React views, but there are some decisions in the ways in which you structure the data in the store state which will determine how you map your state to the component props.

Data

Let’s talk first about the structure of data (as opposed to metadata) in the store state. If you’re working with a REST API, one choice is to store the data exactly as it is returned from the API without modification. Your reducer and state structure could match the resource hierarchy on the backend as well.

Sometimes, however, you may choose to normalize the data, flattening it by id and resource type. As it turns out, this idea is not that well known in the community:

The great Dan Abramov wrote the normalizr module for just this purpose. Not everyone agrees normalization is necessary:

I’d suggest it depends highly on your API and application. For example, some of the nested resources may be different in the context of one parent resource or another, in which case normalization requires choosing one of the returned resources with the same id as “canonical,” which may not be possible.

Metadata

I’m not sure if there are a great number of resources on how to structure metadata in your store, but I have personally tried keeping a separate subtree for requests, delineated by type, updated with their responses when available.

The pros of this approach are:

  • If your requests and request types do not map directly to your data, this structure decouples them.
  • It’s sort of an append-only log for requests made by the application which is useful for displaying different views based on call history, e.g. with a user error that’s validated on the server.

The cons are:

  • It disconnects the request from the data and sometimes you’d like to or need to know which data came from which request.
  • In long-running applications with many calls, you’ll probably need to implement pruning.

React-Refetch has an alternative approach in which they inject the request object itself into the React component with the data, once it’s fetched. The pros and cons are basically reversed from the approach I outlined above.

There’s declarative and then there’s declarative

“Declarative” as we’ve discussed thus far is in the context of “declaring” the next effect that should take place around the Redux store. There is another popular use of “declarative” in the React world more broadly that’s related to declaring which data a component needs, then abstracting the calls to get that data. There are a lot of benefits to this approach including testability, caching, deduplication of calls, and more. React-Refetch does this with a store structured very much like Redux (it was originally forked from Redux).

Of course, Relay is a more well-known approach to the same concept but relies on GraphQL to describe where to get the data (as well as a GraphQL backend, of course). One topic I’d love to hear more about is contrasting how Relay with GraphQL backends handle request metadata and errors with traditional REST backends and an approach like React-Refetch, where the request is injected into the view.

Summary

To summarize the choices and general trends I’ve found:

  • You may or may not want to store your data in Redux, but if you do, you probably want your request metadata in there too
  • For simple apps, imperative fetching might be fine for a while, but declarative approaches are very powerful as you need to sequence asynchronous events
  • You may or may not want to structure your data in normalized form, but there are tools for you to do so if you choose to
  • There appears to be room for more exploration on the topic of how to structure metadata in your store

In other words, the ways that folks are using Redux to fetch and/or store data from the backend are numerous and we’re spoiled for (or burdened by?) choice at the moment. 😊

Appendix: Non-REST backends

If your backend is not REST based, you have some other options:

If you’re interested in working with us, discussing issues like these and others, please check out our jobs page and get in touch!

--

--