In this lesson we're going to see how the Apollo cache looks like and everything is normalised. Queries just reference normalised entities. Entities are cached by their backend database ID plus their typename, to avoid identifier collisions.
Instructor: [0:00] When I click on Categories here, I get a new list of notes for that category. When I click on View, I make a request for a note with that specific ID I clicked on. We've seen how Apollo stores all of this data in the cache as we're using the app. Let's have a look at how that cache works exactly.
[0:18] I'll create an empty object. Let's say I make a query to get all notes with category ID 1. When the response comes back, we'll create a new entry in the cache. I'll give it a meaningful name to make it clear what it contains, and I'll assign my response to it.
[0:36] Sometime later, we make another query, but this time, to get a single note with ID 1. We create another entry in the cache and I'll assign it the response. Notice how it's mostly the same exact response as the note we have on here, but in this query, we asked for a category as well. Let's log this and see how the cache looks like.
[1:01] One problem with storing data like this though, is that we duplicate it. We have the exact same note here and here, but it's stored in two separate places. It's wasting a bit of memory with this content being duplicated.
[1:17] More importantly, what if we wanted to change the content of the note in here. I'll reach into the cache and assign some new content to that note. If I save this and look at what the cache outputs, we'll see that, yes, we correctly updated the content here, but this is still showing the old content.
[1:39] When you store data like this, it's very easy to get into these kind of data inconsistency problems. What if instead of storing full objects in here, we take their ID, create a new entry in the cache just for that ID, and put all the full note info in there. I'll do the same for the second note.
[1:58] In my query, I can just point to these IDs instead of to the full object. I'll point to the first ID, and then also the second ID, and same here. When I ask for a note with this ID from the backend and it gives me back this response, I can see that I already have an entry for ID number 1.
[2:18] Instead of overriding it, I'll just take whatever fields are in there and put all the new fields I got back over it. I'm essentially updating what I already have in the cache. Then instead of the full object, I will just reference the ID.
[2:33] If I remove this and we log out the cache again, if I update this note, then both this query and this one will get the updates because they're just referencing it.
[2:45] Furthermore, when I requested the category with this new query, because my initial notes list was just referencing this by ID, it automatically got access to that new field for free. This is an example of normalized data. We don't have any duplicates in it, and we're using as many references as possible to existing entities.
[3:08] There's just one tiny problem with this approach. If we now make a query to get something else other than notes, let's say categories, and let's say you will get a response like this. If we were to just put this category in the cache like this, it would add a shopping label to a note entry, which is wrong. Notes don't have labels directly. Categories have labels, but not notes.
[3:32] To avoid problems like this, we need our key to be a bit more specific than just the ID. One thing we can do is we can key by type and ID. I'll use note, I'll use note here as well, and then I'll need to change the references as well and everything else.
[3:49] Now that we did this, when we get something like categories, we'll just create a key in the cache of category ID 1, category ID 2. We'll never have two notes with the same ID, and we'll never have two categories with the same ID.
[4:02] If we switch back to our app and we refresh the page, we get two network calls. The first one for the full list of notes that does return me some actual notes, each with an ID, and very importantly, a type name, which is coming from our server-side GraphQL schema. The second request, this is for the single note. We can see in here, we get a note ID and a type name.
[4:30] If you go to the Google Chrome extension store, Apollo client has some dedicated DevTools which we can install. If we go back to the browser now and we look in the Chrome DevTools, we can see there's a tab here for Apollo, which gives us some useful info. Let's look at the cache tab. Here, you can see all of our root queries, and we see our notes query, keyed by the category ID.
[4:57] If we look at the list of notes in it, we can see they're not full notes. They're just references to a note with ID 1, a note with ID 2. If we look at the note query itself, it's also a reference to a note with ID 2. You can then see all the full note information down here, and you have all the content in here.