Working with Normalized Data in Akita and Angular

Netanel Basal
Netanel Basal
Published in
4 min readJul 10, 2018

--

In this article, we’ll build an Angular application which lists movies which arrive from a nested API response. We’ll learn how to store and update our data more efficiently using Akita.

This post assumes that you at least have some working knowledge of Akita. If not, please start with the basics.

Let’s say our server response is:

A movie could have many actors and also many genres. By examining this type of response, we can observe its disadvantages.

  1. Duplicate objects (actors or genres) will increase memory consumption and make the server payload larger.
  2. Updates will be harder and can become very ugly very fast (due to the nature of nested spreads) . For example, if we want to update an actor’s name we’ll need to loop over each movie.actors, check if it exists and update. (the same goes for deletion).

Because of the above reasons, the recommended approach to managing relational or nested data in Akita is to treat your data as if it were in a database and keep that data in a normalized form.

Designing a Normalized State

In Akita, normalizing data means that each entity gets its own “table,” i.e., Akita EntityStore and any references to individual entities should be done by storing the entities’ ID.

An example of a normalized state structure for the movies example above might look like this:

This state structure is much flatter overall. This is an improvement upon the original nested format for several reasons:

  1. We get rid of the duplicate objects, which means our memory consumption decreases and the server payload will be smaller.
  2. We have a single source of truth for each entity which makes updating or removing an entity easier.
  3. Retrieving an entity is just a matter of a simple lookup.
  4. We can query only the data we need. For example, if we have a view that shows only the actors, we can query the ActorsStore and get only the actors without any redundant data.

The recommend option is to perform the normalization on the server-side, so you can also benefit from a smaller payload. I’ve worked in several companies which have developed a generic solution on the server-side. If that’s not possible, you can always do it yourself or leverage a tool like normalizr as we are going to do on the client-side.

Build the Stores

First, let’s create the building blocks for each entity — store and query.

We store each entity in a separate table, i.e. store, and use query to get it.

Now, let’s create the movies component which will be responsible for displaying the movies:

The movies component is relatively straightforward. It queries the movies from MoviesQuery and calls getMovies() to fetch movies from the server.

Let’s examine the getMovies() method:

As I mentioned before, in our example, we’ll use normalizr to flatten the server response. I’m not going to focus on normalizr, but here is the code for our case:

Providing the unflattened response that we saw in the beginning, the above code will return the following structure:

We get the flattened response and update each store with the corresponding data. With actors and movies, we don’t worry about the order, so we call set() with the entities alone, letting Akita resolves the ids.

After initialized our stores, it’s time to create the final query:

That’s what I like to call a reactive join. We are querying the data from each query, mapping each id to its corresponding entity.

Akita Architecture

Let’s stop for a second and explain an important concept.

The combineLatest() observable will invoke the map() projection function whenever any of the selectors (movies, actors, genres) fires. In our case, each time we get a new response from the server, it’ll trigger all three, which leads to redundant executions.

To optimize this behavior, we can use the combineQueries observable. combineQueries is similar to setTimeout(0); It waits for the current execution loop to complete, and only then emits the latest value.

Going back to our component, we can display the movies:

Now, let’s say we want to give users the ability to edit an actor’s name. The only code that we need to run is ActorStore.update() based on the id. For example:

And our reactive join (selectMovies()) will take care of updating each subscriber with the new data.

Follow me on Medium or Twitter to read more about Angular, Akita and JS!

--

--

A FrontEnd Tech Lead, blogger, and open source maintainer. The founder of ngneat, husband and father.