Current section: Loading 4 exercises

Efficient Data Loading in a Remix App


00:00 To get started on this profile route, we're going to import our DB file from apputils.dbserver. So that's the file that's right here, apputils.dbserver. With that, then we can add our loader export. So let's export a function called loader, and this is going to take our data function args.

00:19 So we're going to use the params from data function args, and that's coming from RemixRunNode, and that is a type. We'll just stick that up there. And then we're going to take MartyTheMoneyBag's suggestion to get our user. So again, you're not really here to learn about how to use this DB abstraction,

00:39 so that's why we gave you all this code. But we need the username from somewhere, so let's get the username from our params. So this is very similar to use params, it's just an object that has all the parameters for this route, and the dollar username is how we get that username from the params. All right, awesome.

00:58 And then we're going to return some JSON. So we could return new response and JSON stringify user, and then we'd have our headers, and the content type is application JSON. That is perfectly fine. The default status for response is 200,

01:17 so we could do that also. The problem with doing that is once we get into the UI, our typings don't really know what to do with this new response because this response, really the body for the response could be just about anything. And so for two reasons, we're going to use a utility. One, I don't want to have to do this JSON stringify

01:36 all the time, that's kind of like super annoying, and I don't want to have to specify these values either. And two, the typings will be able to infer things better if, or period, if we use the JSON utility. But I want to show you that because the utility

01:53 is really just creating a new response for us, and there's not a whole lot going on there. Another thing I'm going to do is stick this as an object, just a property on an object. I just like doing that because I feel like it's easier to extend in the future and not have to worry

02:10 about adding more properties onto that user object or anything. So with that, let's update our UI now to access that data that we are returning for this user. So we're going to say, here's our data from useLoaderData that's coming from RemixRunReact.

02:29 We're going to use the generic and pass the type of our loader so that it can infer the return type of this JSON to give us a typed data, which is pretty spiffy, if I, yeah, must say. So coming down here, we're going to swap

02:46 the param's username with data.user. Actually, we want to give the user's name, data.user.name. And then if that doesn't exist for some reason because the name is not required, we're going to say data.user.username. And here you'll notice we have that question mark there.

03:05 That's because the user actually could be null. We could not have a user by that username in the database. We're going to deal with that later. And so I'm just going to ignore the TypeScript errors for now and we'll deal with that in the future. So let's save this and refresh and boom,

03:24 we've got Cody with the uppercase K. So we are in fact getting Cody from the database. So nice job. One thing that we are doing here though, that is maybe not the best. And I'm going to demonstrate that here is if I refresh right here, we'll close this out. And then I go over to Cody's notes,

03:43 you'll see that all the data is loaded for us automatically. So we don't have to worry about this. But one thing to keep in mind is this loader is running on the server. And so that's why we're able to talk to this database directly. We could make an API call downstream, just do like a regular fetch. You can literally just do return fetch,

04:02 you know, myapi.com or whatever, right? Like you can do whatever you want to in here. It's all running on the server. But we're sending the entire user object over here as part of our return value. And so when we do these client side navigations, we're going to take that entire user object and send it over the wire.

04:21 And so that's what's showing up right here. And if we look at the preview, we see the user has created at an email and ID and name and notes and username. That's a lot of stuff that we don't need. We literally just need their name right now, maybe and their username. Maybe in the future, we'll add some other things on here. But yeah, right now we're sending way too much data.

04:42 And so what we can do really easily is just say, name, user.name and username, user.username. And let's, we'll get rid of that. We'll take care of the TypeScript stuff later. So what's, our page still works no problem if we go to the notes page and then navigate back.

05:02 That's a client side navigation. And so we're getting those data requests happening. So let's actually take a look at the network tab when we do that. So right here, now we're literally just sending the data that we need. And this was actually one of the main reasons that people got really excited about GraphQL was because it was really difficult to do what we just did,

05:21 to not load more data than you wanted to. But now we don't have to set up a special endpoint for this or anything like that. We literally just update the loader to send the data that we want, just filter out the stuff we don't want. And then we're off to the races. Now, of course, it would be even better if our database gave us an API

05:40 to not even select more than we needed. And in the future, when we do that, it will, but it is really nice that we have that ability. If we end up getting too much data from the API or something that we can filter it down to just the stuff that our UI needs.

05:57 On top of that, if one of these properties gets changed, so what if we capitalize this, now we're gonna get type safety right here. It's gonna say, hey, listen, you said you had a username property, you don't anymore. And so I'm gonna warn you about that. To me, that's super cool that we get type safety over the network like this.

06:16 So let's move on to our notes page. So right here, we have just a hard-coded note. We want to have like actual notes that are in the database. So we're gonna bring in our database. We're gonna do kind of similar what we've done already. So we're export a function called loader,

06:33 and this is gonna accept our params and we'll have our data function args right here. And then we'll use Marty the money bag suggestions right there and get our username from the params. And then we can return the owner.

06:51 This is JSON, that utility that makes our lives so much better. And yeah, so we'll return the owner and the notes. And then in our UI, we'll get rid of use params and we'll use data from use loader data,

07:10 which will come from your Remix Run React. And we'll say type of loader. And then our owner display name will be data.owner.name or fall back to the data owner username. And again, this could be null. We'll deal with that in a later part of this exercise.

07:32 And then down here, this will be data.owner.username. And then right here, instead of hard coding this nav link, we're going to wrap this inside of a data.notes.map. We'll map those notes and stick all of these inside of there.

07:50 We'll give it a key of note.id. And then we'll fix our parentheses there. There we go. Okay, great. And so now we're getting a bunch of some notes. So we don't want it to say some note. We wanna take the note.title and boom, there we go.

08:09 So we've got Cody's notes. So the proper owner display, that's the capital K. And then we've got all of the notes here. Now, if I select this, we're still, yeah, we're in a bit of trouble here. So let's fix our two here to be note.id. That should fix that.

08:26 And so now we can go between each of these notes. So the last step is just to load the note ID. But of course, one thing that we're kind of overlooking here is reducing the amount of data that we're sending over the wire. So let's take care of that first and then we'll move on to that note page. So this will be notes.map.

08:46 Okay, great. So prettier is formatting. So we know that we're syntax worthy here. And then of course, the owner only needs the name and username as well. So we can say name, owner.name and username, owner.username. And there we go. We are still in a good place.

09:07 Everything is working properly. So last step here should be very familiar. We're gonna say export a function called loader and that gets us our params and we'll say data function args, data function args right here. And then we'll grab this from Marty the money bag.

09:26 We'll return JSON from remix run node. And here's our note. And we get our note ID from the params, const. There we go. And then right here, we get rid of our params and we get our data from the loader.

09:44 So const data, use loader data, type of loader. And then we'll replace this with the title. So data.note.title. And this is the content, data.note.content, boom. There we go. And now we've got all of this data loading automatically. It's awesome.

10:06 And what's cool is we haven't done anything with use state or use effect or anything like that. This is all just working because Remix is emulating the browser. So the browser by default will, as you're navigating between pages, it will do full page refreshes and Remix kind of makes it feel like

10:25 that's what's happening for us because we don't have to worry about managing that state. And that's kind of the objective here with the way Remix is doing this. So to kind of review things, you literally just add a loader to any of your routes. You can do whatever server side things you need to do, talk to a downstream API with private keys,

10:45 whatever you need to do. And then you return the JSON helper and then you can use that JSON value, the serialized JSON in your UI. And you can, like we're doing in here, make a couple of database calls. Now, of course, this isn't super efficient.

11:03 We'll improve this when we actually do data stuff, but you can talk to multiple APIs if you need to. And then you can filter down to just the pieces that are relevant for the UI you're displaying. So you don't have to worry about sending too much data and ends up making a much better user experience because it'll be much faster.

11:21 Now, we do have a couple of TypeScript errors we'll take care of next, but hopefully that gives you an idea of how to load data in a Remix app.