Photo by bruce mars on Unsplash of someone wondering how to deal with paginated API in redux (yes, he really does).

Working with paginated API in redux

Pierre Criulanscy
Spotlight On JavaScript
4 min readAug 3, 2016

--

TL;DR : Use factory functions and reducer composition to reduce boilerplate when dealing with paginated API in redux : redux-paginator

When working with redux, you’re gonna deal with paginated API sooner or later. To avoid writing the same boilerplate over and over, we need to be smart to keep our pagination and entities reducers separated.

The problem

Let’s start by an example, let’s say we want to consume some TodoAPI, the main endpoint for retrieving all todos is…

GET /todos/

…with this response shape :

We want our reducers to reduce that response into this state shape :

We can handle that by this kind of reducer :

So far so good ! But what if we receive hundreds or thousands of todos items from the api ? It’s totally possible to paginate the results client-side, but for efficiency API often provides a paginated endpoint. Here is the one for our TodoAPI :

GET /todos/paginated/(?page=n)

With this response shape for the call /todos/paginated/?page=1 (assuming the API paginates its results by 10 results per page) :

How to deal with this new endpoint ? We could edit our reducer and our actions to handle the pagination but what if we also need to access the previous endpoint ? We need to separate the todos reducer from the one that will handle the pagination.

First attempt : a naive pagination reducer

We need some pagination reducer, maybe to achieve something like the following state shape ?

With this shape, the state is normalized and we can easily selects the todos object for each page. A page is just an object with a list of todos ids and a boolean indicating whether this particular page is being fetch or not. Let’s go for it, we first need to define two actions creators, one for the action of requesting a todo page, and an other one for the action of receiving a todo page :

And the reducers :

Then we can handle the side effects via redux-saga (or thunk middleware if you prefer):

That’s going to work, but we’re totally coupling the pagination to the todos. What if we want to use the same pagination logic for other entities ?

A less naive paginator

Our app is growing and alongside the todos we need to handle a new user entity so our state now looks like this (without pagination) :

We’re now dealing with some new endpoints :

GET /users/
GET /users/(?page=n)

Paginated response shape :

Since our app only contains two different entities, it wouldn’t be too much of a big deal to do some copy / pasting from the above code to handle users pagination. We just need to create new REQUEST_USER_PAGE and RECEIVE_USER_PAGE type, requestUserPage and receiveUserPage actions, and finally create a specific pagination reducer.

But let’s be smarter than that and write some helpers that will eliminate the need for writing such a lot of boilerplate. After all, requesting and receiving a page is the same action type whether it’s relative to users or todos, only the action meta would change. Let’s do that.

The first question we need to answer is about the future shape of our pagination slice and the correlated question of how a pagination could be uniquely identified. How about simply specify a name for each paginator ? :


state = {

pagination: {
todos: { … },
users: { … }
}
}

We now need to figure out what the pagination.todos and pagination.users will look like. What do we need to store for each paginator ?

  • the current page
  • the list of fetched pages

For now, it should be enough, so the whole state shape could be :

Well, how do we create the pagination reducer now ? First, let’s get rid of the requestTodoPage and receiveTodoPage action creators. Instead, we create two new actions creators :

Remember the requestTodoPageWatcher saga above ? We hardcoded in it the endpoint to reach. We need to pass in the requestPage action the endpoint to reach and the response key (response.todos in the receiveTodoPage example above and response.users for the users endpoint) where to extract the results so the saga knows how to retrieve the page and pass the results to the requestPage action creator. We add them in the meta param of the requestPage action :

But now, each time we want to request for a page, we should not forget about specifying the good endpoint ! It’s a bit tedious… Let’s just create a function that builds the correct requestPage, a creator of action creator :

Good ! And what about the pagination reducers now ? We need some reducer factory function to build reducer specific to the endpoint specified for each paginator. Reducing the pages and currentPage is straightforward, we already did that with the todos example above. We just need to wrap this reducer in a factory function that takes the endpoint as an argument in order to escape the reducers quickly if the endpoint does not match the endpoint specified in the action :

Almost done ! Remember the todos reducer ?

How can we handle the items reduction (todos and users) now ? The response is simple : reducer composition. We want to write something like this :

What these hypothetical reducers need to do ? In fact, quite the same things that what we are already doing, i.e : reducing an array of items into a map indexed by ids, in a more generalized way :

Putting it all together

Let’s create a paginator helper function that wraps all the factory functions for a specific endpoint and resultKey :

We can now use it like this :

I actually implemented this pattern (and such other things) in a module you can find under the name redux-paginator. Feedback and suggestions are greatly appreciated !

--

--