Rx.js Fundamentals Fetching from an API
Transcript from the "Fetching from an API" Lesson
>> So now we move towards really everyone's favorite part of frontend engineering, making API requests. Now, I want the record to show that when I found just a reasonably sized collection of dog facts, and I was using the Fetch API, I was not trying to make a pun.
[00:00:25] I also want the record to show I had weeks to avoid making this pun and choosing literally any other dataset and I chose to go with it, so I'm aware. It wasn't intentional, but I committed to it. So yes, we will fetch dog facts. Don't ask me, okay?
[00:00:47] You should have an API, that's no s there, I just gotta change that, but http, api, localhost:3333. If for some reason your API server doesn't work, I also have a version of it hosted as well. They're both options. So we're gonna use, no s, http://localhost:3333, and this API has some fun features.
[00:01:13] Basically, it has query params that may let you make it worse, yeah. What API doesn't need that? So it has the ability to add a delay to it, to have it fail intermittently, and all sorts of fun stuff like that. So we can recreate bad situations. But we have that in place.
[00:01:34] So if I go over here, and we have we have the ability to do stuff asynchronously, we know that from and from fetch give us observables, iterator counter is neat. But like not gonna get you that promotion you're looking for. [LAUGH] So let's look at what this would look like with an API request like a real API.
[00:02:04] So we have this in place and we've got this fetch button on the page, let's go take a look at that UI. We'll start with this fetch button. It'll fetch you some dog facts. Who doesn't like dog facts? So we'll say fetch, which again, I apologize for nothing.
[00:02:58] I care about when these given events change, and here are the places where you need to change them in the DOM is how a bunch of if you've ever looked at those, like what is the fastest frameworks in 2020/2021? A lot of them is how effectively these concepts are being used versus did something change on the walk an entire virtual DOM, right?
[00:03:18] And so the ability to just know what value changed and know where to change it is an incredibly powerful idea. I digress. So we've got merge map in here. And so we're saying when that click comes through, I don't really care what the event is, I wanna merge map, right?
[00:03:36] Because what I wanna do is return a fromFetch. I wanna turn that click into a fetch request. If I just use map, it's gonna be like yeah, here's an observable this we wanted. No, no, I wanted a water by fetch request. I didn't want it observable. So we've got this kind of in place here.
[00:03:56] So we've got mergeMap in this case, and then I don't really care what the button, I've attached it to the right button. I don't care about the event, I don't care about the target value, it's not an input field. If I was doing this on an input field, I might care about the value.
[00:04:14] I don't, I just care that the button was clicked, it's all I'm interested in. And then we've got fromFetch, we'll send it to our end point. Endpoint, endpoint, and then that we're gonna wanna do stuff with. Cuz you know one does not simply use the Fetch API and get back an object like get JSON on a certain perfect library known as jQuery.
[00:04:38] No, one gets a response object which they have to call either .json on, or .blob, or what have you, right? So not only does fetch need to merge map into the request, but with that observable, and let's just put that return there now before I forget. With that observable, we're also gonna need to pipe that through some things, right?
[00:05:05] Cuz fetch, cool, cool, fetch me the response, response to JSON, so on and so forth. So in this case, we will say .pipe. So you think a map or mergeMap?
>> Merge version.
>> Why? What's that?
>> Just a map.
>> All right, so we have one vote for just the map, any votes for mergMap?
[00:05:34] I heard mergeMap until somebody said map, and everybody got quiet.
>> Are we expecting more observables to be-
>> We have SwitchMap.
>> Fetch would not be returning an observable. I haven't looked at that function, though.
>> So a normal fetch, what does a normal fetch return, so we say fetch-
>> That returns a promise. Then that response, response .json is also a promise, right? So I heard you like observables or promises each one. It's interesting. It's not wrong to think about if you were gonna use a .then, mergeMap is probably your best bet, right? Because the chaining of the observables promise it will basically run the values if it is a observable like they'll run through from, right?
[00:06:23] Or it'll treat it like an observable. So in this case, because response.json returns a promise, that also needs to get merge mapped also. Again, the handy mnemonic that I literally just invented right now is, if you're gonna use .then, you'd wanna use a mergeMap. So I've got a pipe here and then we'll mergeMap.
[00:06:44] Yep. Response or JSON, perfect. When prettier is happy with you, then everything mostly works in this case. So then from fetch, We will subscribe, I will say, addFacts should do the trick. There is my dog facts, great. A lot of other things there, cool. So in this case, we are now able to take an event, map it into a promise from a fetch, map that to a response, and show it into the UI.
[00:07:32] But this is great, there's a wonderful API, assuming your server never goes down, right, and nothing bad happens on the Internet. So what do we do in that case, right? We could, for instance, go ahead. And let's add a little bit of delay. And so I, in this case, can modify the API a little bit when I say delay.
[00:08:01] Let's say it takes two seconds to respond. And just to make things worse, we can have this element of chaos, Where kinda like two seconds, like roughly two seconds, cuz again, it's hard to simulate imperfect network conditions if it's always exactly two seconds slow. But this is gonna kinda be a little bit different in this case.
[00:08:32] So we can go ahead and open up the console, just to kinda shrink that down just a little bit. So I can see the network requests cool. As you can see, I request a bunch of them. They kinda come in and they kind of loading at different points all over the place, but these are random dog facts.
[00:08:55] They're gonna get a sampling of them no matter what. What would I wanna use, if I wanna be, we're getting some dog facts, relax, we'll put them on the page when we hear back to the API. We're gonna ignore future clicks until we're done.
>> Exhaust map.
>> Exhaust map, perfect.
[00:09:12] So, let's give that a shot. So, we'll just change this to an exhaust map, And we'll use that instead. You see, I'm going nuts on that button, but I only sent one network request. When that network request comes back, I am welcome to hit the button again. But RX JS will basically ignore clicks until it's done doing what it's doing.
[00:09:41] Random pop quiz. For sure we all know the answer. If I wanted to switch to the newest one, what would I use instead?
>> Switch map.
>> Switch map. Switch map will be like I don't care about the facts I just requested, I want the new facts. They're not real-time or anything, [LAUGH] it doesn't really matter.
[00:09:57] So this case, exhaust map is the right choice for us at this moment. Okay, so what happens if this fails or something? We already have a slow API, it's relatively possible that this API could return an error, all right? So we can make that happen. We got another one called flakiness and a flakiness of 1 will make it fail every time.
[00:10:30] Flakiness of 2 will make it fail every other time. A flakiness of 3, do you get the point? A flakiness of 3 will make it every third time. Let's have it fail all the time right now. So if I just fire this off, and we should be able to see the console.
[00:10:50] You can see that it blows up. It doesn't know what to do, or we got that 500 error. Things are not great. But we could deal with that, right? We could, we have an operator that helps us deal with these things anytime. We saw that an observable ends when it throws an error.
[00:11:07] It's effectually inflation event, we knew how to test for them, we just haven't seen really how to deal with them. Sometimes, it depends on the API, right? It depends on what you wanna do. Sometimes you wanna me an error message, right? Sometimes, you want to try again. These are all fine things to do, but you shouldn't just treat it like everything worked out.
[00:11:30] So we can do in this case is we can go ahead and we have a few options here. Let's see, we could get a little more nuanced here as well. Let's go ahead and say, because from fetch has an interesting behavior. From fetch, if it gets a 400 or 500, that's not a promise rejection, right?
[00:11:52] That is theoretically from fetch or fetch, rather the Fetch API as a whole only catches if there's no network, right? This is a fun Progressive Web App thing, which is you can handle no Internet different than flaky Internet. So let's go ahead, And we'll say, You can do something like, okay, if response is okay, then we return that.
[00:12:28] Now we're using mergeMap, so we do need to return an observable no matter what, right? So we couldn't do something, if you wanna do it kinda like the node style, This would not work because with that switch map or exhaust map, it's expecting that it gets an observable, right?
[00:12:53] This promise, it's gonna turn into an observable. This is just an object.
>> Could you just wrap it off?
>> Yep, [LAUGH] perfect. Yeah, you wrap it in off and now it's an observable. Cool. So we've got that kind of error in place. So that's one way to go ahead and do it.
[00:13:14] And so now when we subscribe, we could deal with that error separately. There's another way to handle this, we'll see in a second, but let's start with this for now, where we could say, Facts can come through or we are gonna kinda almost handle this like Node.js style, where we could say, okay, if there's an error, Do I have a set error helper?
[00:13:46] Yeah, set error with the error message, otherwise, I'll just return as, I don't have to do that. I can say clear out all the facts that were in there cuz we're just kind of appending them before, and we'll say, I think I have bad facts, right? So this is one way to handle it.
[00:14:12] This is good for errors where we wanna do something with it. We still haven't dealt with the idea of what happens if just things go poorly, and we can kinda see that in a little bit as well. And it's one of the advantages that we get from Fetch as well, which it will actually deal with even bad states for us.
[00:14:33] Let's try this out. Make sure I didn't mess anything up. So you can see something went wrong, cuz occasionally API, this one that we still have flakiness set to 1 so it will always fail. Versus if I set that to a 2, it's expecting an object. Fails on purpose, or it's delayed and we get them, right?
[00:14:59] So we can handle both situations.