Data Loading Patterns with JSON API
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Erdi, Balint
Data communication with the API server is a principal design question in rich-client apps. The talk ponders the design angles and gives several examples of data communication between Ember (Data) and a JSON:API compliant backend.
 So, it's great to be here. It's my third time here as an attendee, but my first time as a presenter. And I have to tell you, like, this is a completely different perspective from this side of the stage. So maybe you should try it next year. It's really great. Okay, so I'm going to talk about data loading patterns with just an API, a magic move. And so this talk is going to be a series of use cases that I think are very prevalent in Ember apps. And there's going to be a lot of code involved, so don't say I didn't warn you. And I hope that you, by the end of the presentation, you will have an added sense of confidence in tackling these scenarios. And I also hope that I spare some discussion about some of these because I think that it's really important to just engage in discussion. It's not a trivial topic. So why JSON API first? Well, instead of going into a detailed explanation of all the advantages that JSON API brings you, let me just tell you a story that happened about a month ago. I started a Greenfield project with a team. And so the back-end developer asked me, so what kind of API are we going to use for this project? And I said, let's use JSON API. And then we agreed that we can start a project. There was nothing more to discuss. So of course, down the line, there are going to be things to discuss and agree on, of course. But I really think of JSON API as like the convention over configuration for APIs. So in this sense, I think it's really similar to Ember because it does reduce the number of decisions that you have to make. And I think that's probably the biggest advantage that it has. So to be able to make sense of the examples that follow, I want to make sure that we understand a few things about how Ember data and Ember works. So let's cover these basic things. So in Ember data, you have the store that you mostly interface with and you want to load data. And if the find record, you pass it a type and an ID. And if that record is already in the store, you get it back right away. And then Ember data does a refatch of that record in the background. And if it's not in the store, then you get back a promise. That's eventually going to be resolved with that record. With find all, if there is at least one instance of that type that you are passing in the store, then you get back all of them, all of those that are already in the store. And otherwise, again, you receive a promise that's eventually going to be resolved with all of the records, or everything that the backend returns. The peak methods are different. So with peak record and of course, again, a type and an ID, a peak will never do a request to the backend. So that's the main difference between peak and find methods. So if it's already in the store, you get it back. If it's not in the store, you get no. And with peak all, you get back all the records that are already in the store of that type. And again, never triggering the reload. So that's true by default, I should say. So because there are two flags that affect this behavior, they are should background reload record and should background reload all. And so they are true by default. So EmberData will make these background fetches that I mentioned. But you can set them and you can even set them per type because they are configured on the adapter. Just something to keep in mind. About Ember, I just want to make sure that you understand nested routes and the model hook. So if you have these routes, the main thing to know is that they are always top down. So the parent route is always fully resolved before entering the child route. They are going to leverage this in the examples. And the other thing is that the model hook has this blocking behavior. If you return a promise from the model hook, then it's not going to render the corresponding template until that promise resolves. And all these methods that we saw find record find all and even query, they all return promises. So they are very natural fit to put these two together if you want this blocking behavior. And the app that I'm going to show these examples on is a music catalog app that some of you might already know. So it has a list of bands on the left. And then the next level, the bands.band route, we just render that tab navigation. And then finally, the songs route, we render everything else. So the list of songs, those buttons to sort the songs and the search box. And the routes are nested as below. So a little bit more about myself. I'm, I'm byline terdy. On Twitter, I'm BAAZ. And I have a personal site and blog at bylineterdy.com. I work as an AmberJS consultant. And I'm also the author of the Rock and Roll with AmberJS book. That's meant for developers who want to conquer the Amber learning curve. Okay. So with that out of the way, let's see the actual case studies. The first broad category of data loading scenarios that I'm going to show you are related to fetching relationship data. And so there are lots of ways to do that. And some developers have their own preferred way of doing that. So let's see some of these techniques and what the advantages and disadvantages for each of them are. The first one is what I call lazy fetching, which is the default behavior in Amber data. So if you have a relationship between bands and songs, a band has many songs. And then you have the following routes. In the band route, you, of course, that's like the classic Amber route, Amber model hook. You just grab that one band, which is designated by its ID. And then in the, in the songs template, where you still have the model is still the band, you say model.songs. So it's only in this moment when Amber renders that template that this relationship is going to be fetched. So let's see what that looks like in practice. You can see that if I click one of the bands to see their songs, then it's, there is like a blank page with an empty list. And then when all of the songs are returned from the API from the backend, the list is, of course, re-undered with, with the, with the songs. So I think definitely the advantage is that it's very simple and it just works. And you never fetch data that you don't need. So you don't fetch the songs of a band, which is not visited. On one downside, though, is that you don't really control. You didn't really think about how this data is loaded. So you can totally trigger and request in this case. So if a band has 10, 10 songs or 20 songs, then you might trigger 20 songs, like song slash one song, song slash two and so on. And there is also this disturbing flicker that is not ideal. So let's see whether we can do something about that. The models stay the same. But what we do now in the, in the songs route is that not only we load the band or just grab the band, but we also already load the related songs. And by virtue of having a promise returned, and BrowRCP hash returns a promise, you make sure that you are blocking the rendering of the template until all of the data is in. So now if we check that again, you will see that indeed, you can even see the spinner, so the data is loading, and then you see the full list. So there is no flicker. So the takeaway here is that if you want this blocking behavior, then one way to do that is you just move everything that you want to load before you render a template into the model. So that's, I think, definitely a better user experience. You still have that, the risk of triggering and requests, however. And also, I think it's a slide disadvantage, but RSVP hashes, by some it's considered like an anti-pattern in Malo Hooks. So let's see another way of removing the flicker. And now we will leverage JSON API. So if we pull up the spec, it says an endpoint may also support an include request parameter to allow the client to customize which related resources should be returned. Okay, so we need to use the include parameter when we make a request to the backend. And number data has first class support for that. You can just pass the include option and then the name of the related, the name of the relationship. So that's what we are doing in the band route. So now I'm fetching the band, but we tell the API to send us also the related songs. This is what it looks like, the actual request look like. And what the API returns is that under data you have the primary source, but you can see that you have also an included key. And there you have the related resources. This is by the way called compound documents in the specification. So we might think that now we are done. We are removing the flicker. Again, if I click let's zappel in, the songs are loaded, but actually the flicker is not removed. You can see, first you can see the empty list and only after that the full correct list is rendered. So why is that? You might have thought that by siloing the songs we again blocked the rendering of the template by returning it as a promise, but it's not. Well, it turns out that if you remember the find record, we are returned immediately if that band is already in the store. And it is already because we have nested routes and we loaded all the bands for the list of bands on the left. So the fact that we pass in the include songs doesn't alter that behavior. So we need to tell Ember data to reload that record already even if it's already in the store. And we do that by passing reload equals true to find record. And with that amendment, it's now working as we wanted to. So here we leverage JSON API. That's a positive thing. And I think another advantage is here a developer had to think about siloing things. So you have more explicit control over how you load that data. However, by passing that reload true option, we delay the rendering of the band template because if you didn't pass that, then it would have returned immediately. Now this is no longer the case. So in this application, this is not a biggie because we don't really render anything in the band template, but your user case might be different. And I also want to mention the references API, which is available in Ember data since version 2.5. It's another great way to check whether a relationship is loaded to load it or condition not loaded. So, and the takeaway from all of these scenarios that we saw for loading relationships is that I think that data loading such that it should be as much a part of the design process as much as, for example, designing your UI. So it shouldn't be an afterthought or shouldn't just say that it works. Okay. Let's leave relationships behind us. And then focus on searching on the back end. So here the specification says that the filter query parameter is reserved for filtering data. Servers and clients should use this key for filtering operations. Okay. So since searching is a form of filtering data, we'll use that. So you can see that I modified the model hook for the song's route. Now if there is a search term that I'm lying 13, I'm now doing a query for songs and I'm passing in the search term. I also wrap this in a filter key. And Ember data does the writing. I mean, it just serializes this to the right request that you can see at the bottom. There is one problem, though, is that this does a global search. Let's say that Zepelin doesn't have a song called Daughter. That's how I knew that something was wrong. So we have to modify this code to restrain the search through those related to the current band. And I did this with, I did this by passing in the band ID also. And then in the adapter, I'm composing the URL so that the URL is the relationship URL that is queried. So it's going to be something like band slash 24 slash songs and then the search term as a filter. And with this, it now works correctly. So really just the search is just for the band whose songs we are looking at. So if you want to do some searching, then use the filter parameter, sorry, the filter parameter in JSON API. Sorting is another prevalent thing in Ember applications and JSON API is something to say about these two. An endpoint may support request to sort the primary data with a sort query parameter. The value for sort must represent sort fields. The sort order for each sort field must be ascending unless it's prefix with a minus sign in which case it must be descending. Okay, so we have to use the sort query parameter to sort the data. So in the band's route, I didn't change the code. I'm going to tackle this in the adapter layer. In the songs route, though, I'm passing in the sort because I want to be able to modify the sorting criteria by clicking on one of these buttons. And then in the template, I'm just switching the value of that sort by parameter that changes this. And so in the adapter layer, in the case of the bands, it's just a static value. So I can just pass in sort equals name. And then in the for songs, so in the song adapter, I'm adding sort equals sort to the URL. So that sort comes from the query call to the store. You can see that in the first case, I'm setting the value of the sort as minus rating slash plus or a comma title. So that field first sort them in ascending order, sorry, in descending order of their rating. And then if there is a tie, I use the title to break it up. And this works perfectly. They use the JSON API sort parameter and you don't even have to transform it. You can just use it directly in your templates and controllers and facilities to the back end. Now, pagination. Again, we take a look at the spec. Server may choose to limit the number of resources returned in a response to a subset of the whole set available. JSON API is agnostic about the pagination strategy used by a server. The page creep parameter can be used as a basis for any of these strategies. So we are going to use a page parameter where specified, a meta member can be used to include nonstandard meta information. The value of each meta member must be an object. So we are going to use the meta object to contain the information about how many records there are in the collection. So an example of this is that if the back end returns the songs, it can also include a meta object saying how many pages and records there are altogether. And so my adapter is growing larger and larger, but now I can add the page parameter, page number and page size. Both of these are necessary. And then in the same way as before, I'm from the route. I'm passing in the page number. The page size is now just set to 10. And then it's really easy to assemble our own pagination links because it's quite trivial to do has previous page and has next page and then just use these in the template to see whether the previous and the next page links need to be rendered. And I'm just passing in for creep in the page number, of course, just decrease or increase the page number. Again, now I added a few more songs so we can see that there are at least two pages and we can see if pagination works correctly. And it does. And I can even search in the same time. So the URLs are getting bigger and bigger, but we don't really care about that and we are doing lots of things. So sorting, searching and pagination are all included now. So use just an API page parameter and the pagination metadata. So that closes that section of the scenarios. And I want to end with a scenario that intrigued me for quite a while. So we know that if you want to indicate to the user set that some data is being loaded from the back end and this is done in the foreground, so to speak, then it's relatively easy to do that with the loading substrate, the corresponding loading template or the loading event depending on your needs. But I think what is harder to do is to do the same thing when Amber data is fetching data in the background. And I came across this. Came across this problem sometime, I wanted to solve it. So to able to demonstrate this, I added two more routes, the application called artists and artists. The important thing about these is that they are not nested. So it's possible to load just one artist to the store without loading all of them, which was not possible before with nested routes. There is nothing fancy going on in the routes, though. I'm just fetching all of the artists in artist.js and I'm fetching the one artist which is specified in the URL in artist.js. And the template, again, nothing really special about that. The only thing is that I want to show a spinner when the is loading artist flag is true. So that's the flag that we should set correctly. So it looks like this, again, a list of artists on the left. And now if you click one of the artists, then the page is taken over by that page, the detailed information about a single artist. So the following scenario then becomes possible. You start on the detailed page and then you go back to the list of artists. And what you see is just one single artist, the one that we booted up the app on. And I think that's quite perplexing to the user. I mean, they can be terrified where all the artists have gone. So that's where I want to improve the user experience and show the spinner that data fetching is going on. So by the way, this is the case, again, because of the eager return behavior of find all. So if you remember, if find all finds at least one record already in the store, then it will return immediately. And that's what we can see here, because that one artist is already loading. And it does then a background refetching of all the records. So what I did is that in the model hook, I first see with p call that there are any artists in the store. So this will not trigger a request to the backend. And if there are none, then I can fetch them all via find all, because I know that's going to block the model hook. And I can show them a smear to user. But if there is at least one, then I return all of them on line nine. And at the same time, I schedule fetching all the artists for later. And fetch more artists, what it does is that now I can set that load flag to true. And I can then fetch all of the artists with find all passing in against reload true, because we know that there's at least one artist, so you don't want an immediate return. And once that's back from the backend response, then we can set the model to this new value, to the full list of artists. And I shouldn't, of course, forget to reset the load in flag to false. So let's see now. So we start with flee. And then once we go back to the artist, we now have this nice spinner, so the user is relieved. So what I did essentially is that I deconstructed the background data fetching process just to have more control and be able to set loading flags. And I wasn't quite satisfied with that. So that gave me an opportunity to do something with Ember concurrency that had been meaning to do for a while. And just maybe two sentences about Ember concurrency, it's a great add-on for managing asynchronous tasks. And you can do, you can really do some things that would be very, very complicated or outright impossible with promises. It does a lot more, but that's the important thing here. So now with Ember concurrency, what I did is that I now return a simple JavaScript object from the model hook. The reason I'm doing that is just to have it returned immediately. And then I set, I defined a number concurrency task called fetch artist task. And so I'm initiating one instant of that task by calling perform on it. So after that, the setup controller will be executed. And then again, with a peak call, I'm checking if there are any artists. And I'm setting those artists already on the loaded artist. That's so I'm able to show to the user some artists already. And then I introduce like an artificial timeout in the fetch artist task. It's on line 20. Just to be able to see the spinner. And then I can now do the find or call in that task. So now in the controller, what I'm doing is the artist's property is going to be the most important part here. I can check, and that's why I'm doing on line 10, whether that Ember concurrency task has already finished. If it has, then I can return its value. So its value is going to be whatever was returned from the fetch artist task in the upper left, which in this case is going to be the full list of artists. But if it's not finished yet, then I already have my loaded artist, and I can just return this. And so that's what I'm iterating through in the template. That's going to do the right thing. And another added bonus if we use Ember concurrency is that it has all of these status flags on the task, so I don't have to do that manual. So I can just use the task that is running to be able to know whether are we still fetching data. So I think that's a superior solution because there is, I didn't need to manually load the flags. Just manually set the flags. There was no run loop scheduling involved, and I don't even have to pass the reload through option to find all. So one more thing before we wrap up is that I prepared a page on my site just for you. It contains the slides for this presentation, but it also contains a pointer to a GitHub repository, which includes all of these scenarios, and then some more, which I didn't have time to cover here. And on top of that, there is also a mini-book that I assembled from these examples. So if you'd like to learn more about this then, check out that site. And thank you very much for your sustained attention. Thank you.
 So, it's great to be here. It's my third time here as an attendee, but my first time as  a presenter. And I have to tell you, like, this is a completely different perspective  from this side of the stage. So maybe you should try it next year. It's really great.  Okay, so I'm going to talk about data loading patterns with just an API, a magic move. And  so this talk is going to be a series of use cases that I think are very prevalent in Ember  apps. And there's going to be a lot of code involved, so don't say I didn't warn you. And  I hope that you, by the end of the presentation, you will have an added sense of confidence  in tackling these scenarios. And I also hope that I spare some discussion about some of  these because I think that it's really important to just engage in discussion. It's not a trivial  topic. So why JSON API first? Well, instead of going into a detailed explanation of all  the advantages that JSON API brings you, let me just tell you a story that happened about  a month ago. I started a Greenfield project with a team. And so the back-end developer  asked me, so what kind of API are we going to use for this project? And I said, let's  use JSON API. And then we agreed that we can start a project. There was nothing more to  discuss. So of course, down the line, there are going to be things to discuss and agree  on, of course. But I really think of JSON API as like the convention over configuration  for APIs. So in this sense, I think it's really similar to Ember because it does reduce the  number of decisions that you have to make. And I think that's probably the biggest advantage  that it has. So to be able to make sense of the examples that follow, I want to make sure  that we understand a few things about how Ember data and Ember works. So let's cover  these basic things. So in Ember data, you have the store that you mostly interface with  and you want to load data. And if the find record, you pass it a type and an ID. And  if that record is already in the store, you get it back right away. And then Ember data  does a refatch of that record in the background. And if it's not in the store, then you get  back a promise. That's eventually going to be resolved with that record. With find all,  if there is at least one instance of that type that you are passing in the store, then  you get back all of them, all of those that are already in the store. And otherwise, again,  you receive a promise that's eventually going to be resolved with all of the records, or  everything that the backend returns. The peak methods are different. So with peak record  and of course, again, a type and an ID, a peak will never do a request to the backend.  So that's the main difference between peak and find methods. So if it's already in the  store, you get it back. If it's not in the store, you get no. And with peak all, you  get back all the records that are already in the store of that type. And again, never  triggering the reload. So that's true by default, I should say. So because there are two flags  that affect this behavior, they are should background reload record and should background  reload all. And so they are true by default. So EmberData will make these background fetches  that I mentioned. But you can set them and you can even set them per type because they  are configured on the adapter. Just something to keep in mind. About Ember, I just want  to make sure that you understand nested routes and the model hook. So if you have these routes,  the main thing to know is that they are always top down. So the parent route is always fully  resolved before entering the child route. They are going to leverage this in the examples.  And the other thing is that the model hook has this blocking behavior. If you return  a promise from the model hook, then it's not going to render the corresponding template  until that promise resolves. And all these methods that we saw find record find all and  even query, they all return promises. So they are very natural fit to put these two together  if you want this blocking behavior. And the app that I'm going to show these examples  on is a music catalog app that some of you might already know. So it has a list of bands  on the left. And then the next level, the bands.band route, we just render that tab  navigation. And then finally, the songs route, we render everything else. So the list of  songs, those buttons to sort the songs and the search box. And the routes are nested  as below. So a little bit more about myself. I'm, I'm  byline terdy. On Twitter, I'm BAAZ. And I have a personal site and blog at bylineterdy.com.  I work as an AmberJS consultant. And I'm also the author of the Rock and Roll with AmberJS  book. That's meant for developers who want to conquer the Amber learning curve.  Okay. So with that out of the way, let's see the actual case studies. The first broad category  of data loading scenarios that I'm going to show you are related to fetching relationship  data. And so there are lots of ways to do that. And some developers have their own preferred  way of doing that. So let's see some of these techniques and what the advantages and disadvantages  for each of them are. The first one is what I call lazy fetching,  which is the default behavior in Amber data. So if you have a relationship between bands  and songs, a band has many songs. And then you have the following routes. In the band  route, you, of course, that's like the classic Amber route, Amber model hook. You just grab  that one band, which is designated by its ID. And then in the, in the songs template,  where you still have the model is still the band, you say model.songs. So it's only in  this moment when Amber renders that template that this relationship is going to be fetched.  So let's see what that looks like in practice. You can see that if I click one of the bands  to see their songs, then it's, there is like a blank page with an empty list. And then when  all of the songs are returned from the API from the backend, the list is, of course,  re-undered with, with the, with the songs. So I think definitely the advantage is that  it's very simple and it just works. And you never fetch data that you don't need. So you  don't fetch the songs of a band, which is not visited. On one downside, though, is that  you don't really control. You didn't really think about how this data is loaded. So you  can totally trigger and request in this case. So if a band has 10, 10 songs or 20 songs,  then you might trigger 20 songs, like song slash one song, song slash two and so on.  And there is also this disturbing flicker that is not ideal. So let's see whether we  can do something about that. The models stay the same. But what we do now in the, in the  songs route is that not only we load the band or just grab the band, but we also already  load the related songs. And by virtue of having a promise returned, and BrowRCP hash returns  a promise, you make sure that you are blocking the rendering of the template until all of  the data is in. So now if we check that again, you will see that indeed, you can even see  the spinner, so the data is loading, and then you see the full list. So there is no flicker.  So the takeaway here is that if you want this blocking behavior, then one way to do that  is you just move everything that you want to load before you render a template into  the model. So that's, I think, definitely a better user experience. You still have that,  the risk of triggering and requests, however. And also, I think it's a slide disadvantage,  but RSVP hashes, by some it's considered like an anti-pattern in Malo Hooks. So let's see  another way of removing the flicker. And now we will leverage JSON API. So if we pull up  the spec, it says an endpoint may also support an include request parameter to allow the  client to customize which related resources should be returned. Okay, so we need to use  the include parameter when we make a request to the backend. And number data has first  class support for that. You can just pass the include option and then the name of the  related, the name of the relationship. So that's what we are doing in the band route.  So now I'm fetching the band, but we tell the API to send us also the related songs.  This is what it looks like, the actual request look like. And what the API returns is that  under data you have the primary source, but you can see that you have also an included  key. And there you have the related resources. This is by the way called compound documents  in the specification. So we might think that now we are done. We are removing the flicker.  Again, if I click let's zappel in, the songs are loaded, but actually the flicker is not  removed. You can see, first you can see the empty list and only after that the full correct  list is rendered. So why is that? You might have thought that by siloing the songs we  again blocked the rendering of the template by returning it as a promise, but it's not.  Well, it turns out that if you remember the find record, we are returned immediately if  that band is already in the store. And it is already because we have nested routes and  we loaded all the bands for the list of bands on the left. So the fact that we pass in the  include songs doesn't alter that behavior. So we need to tell Ember data to reload that  record already even if it's already in the store. And we do that by passing reload equals  true to find record. And with that amendment, it's now working as we wanted to.  So here we leverage JSON API. That's a positive thing. And I think another advantage is here  a developer had to think about siloing things. So you have more explicit control over how  you load that data. However, by passing that reload true option, we delay the rendering  of the band template because if you didn't pass that, then it would have returned immediately.  Now this is no longer the case. So in this application, this is not a biggie because we don't really  render anything in the band template, but your user case might be different. And I also  want to mention the references API, which is available in Ember data since version 2.5.  It's another great way to check whether a relationship is loaded to load it or condition  not loaded. So, and the takeaway from all of these scenarios that we saw for loading  relationships is that I think that data loading such that it should be as much a part of the  design process as much as, for example, designing your UI. So it shouldn't be an afterthought  or shouldn't just say that it works. Okay. Let's leave relationships behind us. And then  focus on searching on the back end. So here the specification says that the filter query  parameter is reserved for filtering data. Servers and clients should use this key for  filtering operations. Okay. So since searching is a form of filtering data, we'll use that.  So you can see that I modified the model hook for the song's route. Now if there is a search  term that I'm lying 13, I'm now doing a query for songs and I'm passing in the search term.  I also wrap this in a filter key. And Ember data does the writing. I mean, it just serializes  this to the right request that you can see at the bottom. There is one problem, though,  is that this does a global search. Let's say that Zepelin doesn't have a song called  Daughter. That's how I knew that something was wrong. So we have to modify this code  to restrain the search through those related to the current band. And I did this with,  I did this by passing in the band ID also. And then in the adapter, I'm composing the  URL so that the URL is the relationship URL that is queried. So it's going to be something  like band slash 24 slash songs and then the search term as a filter. And with this, it  now works correctly. So really just the search is just for the band whose songs we are looking  at. So if you want to do some searching, then use the filter parameter, sorry, the filter  parameter in JSON API. Sorting is another prevalent thing in Ember applications and  JSON API is something to say about these two. An endpoint may support request to sort the  primary data with a sort query parameter. The value for sort must represent sort fields.  The sort order for each sort field must be ascending unless it's prefix with a minus  sign in which case it must be descending. Okay, so we have to use the sort query parameter  to sort the data. So in the band's route, I didn't change the code. I'm going to tackle  this in the adapter layer. In the songs route, though, I'm passing in the sort because I  want to be able to modify the sorting criteria by clicking on one of these buttons. And then  in the template, I'm just switching the value of that sort by parameter that changes this.  And so in the adapter layer, in the case of the bands, it's just a static value. So I  can just pass in sort equals name. And then in the for songs, so in the song adapter,  I'm adding sort equals sort to the URL. So that sort comes from the query call to the  store. You can see that in the first case, I'm setting the value of the sort as minus  rating slash plus or a comma title. So that field first sort them in ascending order,  sorry, in descending order of their rating. And then if there is a tie, I use the title  to break it up. And this works perfectly. They use the JSON API sort parameter and you  don't even have to transform it. You can just use it directly in your templates and controllers  and facilities to the back end. Now, pagination. Again, we take a look at the spec. Server may  choose to limit the number of resources returned in a response to a subset of the whole set  available. JSON API is agnostic about the pagination strategy used by a server. The  page creep parameter can be used as a basis for any of these strategies. So we are going  to use a page parameter where specified, a meta member can be used to include nonstandard  meta information. The value of each meta member must be an object. So we are going  to use the meta object to contain the information about how many records there are in the collection.  So an example of this is that if the back end returns the songs, it can also include  a meta object saying how many pages and records there are altogether. And so my adapter is  growing larger and larger, but now I can add the page parameter, page number and page size.  Both of these are necessary. And then in the same way as before, I'm from the route. I'm  passing in the page number. The page size is now just set to 10. And then it's really  easy to assemble our own pagination links because it's quite trivial to do has previous  page and has next page and then just use these in the template to see whether the previous  and the next page links need to be rendered. And I'm just passing in for creep in the page  number, of course, just decrease or increase the page number. Again, now I added a few more  songs so we can see that there are at least two pages and we can see if pagination works  correctly. And it does. And I can even search in the same time. So the URLs are getting  bigger and bigger, but we don't really care about that and we are doing lots of things.  So sorting, searching and pagination are all included now. So use just an API page parameter  and the pagination metadata. So that closes that section of the scenarios. And I want  to end with a scenario that intrigued me for quite a while. So we know that if you want  to indicate to the user set that some data is being loaded from the back end and this  is done in the foreground, so to speak, then it's relatively easy to do that with the loading  substrate, the corresponding loading template or the loading event depending on your needs.  But I think what is harder to do is to do the same thing when Amber data is fetching  data in the background. And I came across this. Came across this problem sometime, I  wanted to solve it. So to able to demonstrate this, I added two more routes, the application  called artists and artists. The important thing about these is that they are not nested.  So it's possible to load just one artist to the store without loading all of them, which  was not possible before with nested routes. There is nothing fancy going on in the routes,  though. I'm just fetching all of the artists in artist.js and I'm fetching the one artist  which is specified in the URL in artist.js. And the template, again, nothing really special  about that. The only thing is that I want to show a spinner when the is loading artist  flag is true. So that's the flag that we should set correctly. So it looks like this, again,  a list of artists on the left. And now if you click one of the artists, then the page  is taken over by that page, the detailed information about a single artist. So the following scenario  then becomes possible. You start on the detailed page and then you go back to the list of artists.  And what you see is just one single artist, the one that we booted up the app on. And  I think that's quite perplexing to the user. I mean, they can be terrified where all the  artists have gone. So that's where I want to improve the user experience and show the  spinner that data fetching is going on. So by the way, this is the case, again, because  of the eager return behavior of find all. So if you remember, if find all finds at least  one record already in the store, then it will return immediately. And that's what we can  see here, because that one artist is already loading. And it does then a background refetching  of all the records. So what I did is that in the model hook, I first see with p call  that there are any artists in the store. So this will not trigger a request to the backend.  And if there are none, then I can fetch them all via find all, because I know that's going  to block the model hook. And I can show them a smear to user. But if there is at least  one, then I return all of them on line nine. And at the same time, I schedule fetching  all the artists for later. And fetch more artists, what it does is that now I can set  that load flag to true. And I can then fetch all of the artists with find all passing in  against reload true, because we know that there's at least one artist, so you don't  want an immediate return. And once that's back from the backend response, then we can  set the model to this new value, to the full list of artists. And I shouldn't, of course,  forget to reset the load in flag to false. So let's see now. So we start with flee.  And then once we go back to the artist, we now have this nice spinner, so the user is  relieved. So what I did essentially is that I deconstructed the background data fetching  process just to have more control and be able to set loading flags. And I wasn't quite satisfied  with that. So that gave me an opportunity to do something with Ember concurrency that  had been meaning to do for a while. And just maybe two sentences about Ember concurrency,  it's a great add-on for managing asynchronous tasks. And you can do, you can really do some  things that would be very, very complicated or outright impossible with promises. It does  a lot more, but that's the important thing here. So now with Ember concurrency, what  I did is that I now return a simple JavaScript object from the model hook. The reason I'm  doing that is just to have it returned immediately. And then I set, I defined a number concurrency  task called fetch artist task. And so I'm initiating one instant of that task by calling  perform on it. So after that, the setup controller will be executed. And then again, with a peak  call, I'm checking if there are any artists. And I'm setting those artists already on the  loaded artist. That's so I'm able to show to the user some artists already. And then  I introduce like an artificial timeout in the fetch artist task. It's on line 20. Just  to be able to see the spinner. And then I can now do the find or call in that task. So  now in the controller, what I'm doing is the artist's property is going to be the most  important part here. I can check, and that's why I'm doing on line 10, whether that Ember  concurrency task has already finished. If it has, then I can return its value. So its  value is going to be whatever was returned from the fetch artist task in the upper left,  which in this case is going to be the full list of artists. But if it's not finished  yet, then I already have my loaded artist, and I can just return this. And so that's  what I'm iterating through in the template. That's going to do the right thing. And another  added bonus if we use Ember concurrency is that it has all of these status flags on the  task, so I don't have to do that manual. So I can just use the task that is running to  be able to know whether are we still fetching data. So I think that's a superior solution  because there is, I didn't need to manually load the flags. Just manually set the flags.  There was no run loop scheduling involved, and I don't even have to pass the reload through  option to find all. So one more thing before we wrap up is that I prepared a page on my  site just for you. It contains the slides for this presentation, but it also contains  a pointer to a GitHub repository, which includes all of these scenarios, and then some more,  which I didn't have time to cover here. And on top of that, there is also a mini-book  that I assembled from these examples. So if you'd like to learn more about this then,  check out that site. And thank you very much for your sustained attention.  Thank you.
Data_Loading_Patterns_with_JSON_API_0_935.png Data_Loading_Patterns_with_JSON_API_936_3095.png Data_Loading_Patterns_with_JSON_API_3096_5927.png Data_Loading_Patterns_with_JSON_API_5928_7751.png Data_Loading_Patterns_with_JSON_API_7752_8399.png Data_Loading_Patterns_with_JSON_API_8400_9755.png Data_Loading_Patterns_with_JSON_API_9756_10667.png Data_Loading_Patterns_with_JSON_API_10668_11195.png Data_Loading_Patterns_with_JSON_API_11196_12791.png Data_Loading_Patterns_with_JSON_API_12792_13127.png Data_Loading_Patterns_with_JSON_API_13128_15731.png Data_Loading_Patterns_with_JSON_API_15732_16043.png Data_Loading_Patterns_with_JSON_API_16044_20399.png Data_Loading_Patterns_with_JSON_API_20400_20471.png Data_Loading_Patterns_with_JSON_API_20472_21119.png Data_Loading_Patterns_with_JSON_API_21120_21371.png Data_Loading_Patterns_with_JSON_API_21372_22211.png Data_Loading_Patterns_with_JSON_API_22212_22931.png Data_Loading_Patterns_with_JSON_API_22932_23459.png Data_Loading_Patterns_with_JSON_API_23460_24047.png Data_Loading_Patterns_with_JSON_API_24048_25571.png Data_Loading_Patterns_with_JSON_API_25572_29303.png Data_Loading_Patterns_with_JSON_API_29304_29663.png Data_Loading_Patterns_with_JSON_API_29664_30011.png Data_Loading_Patterns_with_JSON_API_30012_30707.png Data_Loading_Patterns_with_JSON_API_30708_30911.png Data_Loading_Patterns_with_JSON_API_30912_32591.png Data_Loading_Patterns_with_JSON_API_32592_32819.png Data_Loading_Patterns_with_JSON_API_32820_33179.png Data_Loading_Patterns_with_JSON_API_33180_34127.png Data_Loading_Patterns_with_JSON_API_34128_36959.png Data_Loading_Patterns_with_JSON_API_36960_38447.png Data_Loading_Patterns_with_JSON_API_38448_38531.png Data_Loading_Patterns_with_JSON_API_38532_38675.png Data_Loading_Patterns_with_JSON_API_38676_38717.png