-
Notifications
You must be signed in to change notification settings - Fork 0
HATEOAS
In this module we're going to be talking about HATEOAS or Hypertext as the Engine of Application State. We've been working through this process of building out a RESTful API with Node and Express. We've built all the verbs, we've got a book API going, we spend last module talking about testing and integration tests versus unit tests, and how all that works. There's one more constraint that I want to talk about today and that's HATEOAS and how using a hypermedia in our application helps to build this self documenting API that becomes very easy for someone to navigate and understand what options are available to them at any time while they're using your API.
Now here we have the API that we just built. It's /api/books, it returns back a list of books that we have in our MongoDB database and you've seen me do this over the course of this entire course, when I want an individual item I just copy that id and I paste it up there, and that brings me to an individual book and I know that because I wrote the API, and you know that because I told you that, but all of the other consumers of this API, if we were to stick this API out on the internet, wouldn't understand intuitively that that's the right way to get to a single book because it could be /title, it could be /isbn number, it could be /something else. There's no way to intuitively just know how to get to an individual book when you're coming from a list of books, and so what HATEOAS or this hypermedia attempts to do or one of the problems it's trying to solve is to give your API away to self-document, so that a consumer of your API knows exactly what to do to get to this individual item or what other options are available, so let's pop over to our code and start to write some of these hypertext links into our API, so we can see what that looks like.
Now in order to get our hyperlinks in our API and our return we need to first start with our route, so if we go into bookRoutes.js you'll see that our .get just /api/books comes from our bookController, so if we pop over to bookController and our var get we need to interrupt this down here and insert links into the books that come back from MongoDB. Now unfortunately really the only way to do this is to actually physically loop through the books and append links on, so the best way to do that for us is going to be to use a forEach loop (Typing), and as we loop through this forEach loop we'll append on links to the books. Now a forEach takes a function with a couple of parameters. The first one's going to be element, index, and array. Now ultimately what we'd like to do is just change the element, so if we do element.links, and then add links on right here, but what we have to remember is that these are not just the JSON object that you get back, these are actually Mongoose models, so I can't change what those elements are without adjusting the model because every time I add something here Mongoose is going to evaluate that against this bookModel that we have and notice we don't have links in our bookModel. I'd rather not muddy the waters with our Mongoose model by adding links in here at this point. I'd rather find a different way to add links on here, so we can't do it just straight like this, we actually have to create a newBook and copy the element over, and if we do .toJSON that will actually do what we're looking for. It'll strip out all of the Mongoose stuff and just leave us with the JSON object that we can then start to edit. Now if we're going to do it this way though what we're going to need to do I create a new array, so let's do an array called returnBooks and then we'll push on our newBook, so each item in the array we'll do a returnBooks.push and we'll add the newBook. Then in the end we'll just res.json our returnBooks. Now what we're going to want to do is we're going to want to create a section of our newBook called links and this is where we're going to add all of our hyperlinks onto our objects and this is going to be the same throughout our entire API. If you want to know what else you can do with this object you look in the links section and it's just going to be a big full list. Now, in this case, we're just going to add one called self, so we're going to do newBook.links.self and we're going to set that equal to the link to the individual item for this book, and so we'll do http:// and we're going to pull the rest out of the request header, so req.headers.host, and that's going to give us that local host 3000 URL that we can just go click on. Then we're going to want to add /api/books/ + newBook._id and what that's going to do now is for every book in our return it's going to send us back the book plus a link to get to that individual book, so we'll clean this up, that should be equals not a semicolon there, we'll save that, pop over here. Now when I refresh this you'll see now I have links with a link to the individual item, so if I scroll down to, and we'll just pick one, let's go to The Dark World, I click on that, and now it takes me to the individual item, so now as a consumer of the API I just know how this works, I know how to move to an individual item, I click on that, and there we go. Now if you also remember when we're searching and we're filtering this list we didn't allow you to filter by everything, we only allowed you to filter by some items, so let's add a link on this page that helps us understand what other options we have here. Let's go back over to our code and we didn't ever move our individual items over into our bookController, so we'll make those changes here in bookRoutes. If you look at the .get all we're doing is returning the book, so there's really not a whole lot of functionality here, but instead what we're going to want to do is do var returnBook = req.book.toJSON, and then we want to do that same thing, so we want to do returnBook.links (Typing), and we have to add this step because if I just do .links.self, well links doesn't exist yet, so I can't --- add something to something that doesn't exist, I'll get an undefined error, so we've got to do this stuff first, and then we're going to do returnBook.links. we're going to call it FilterByGenre, let's do FilterByThisGenre. What that's going to do is it's going to create a link and we'll just copy this one (Typing), it's going to create a link that has a filtered list based upon that genre, so it could be other books by the genre or however we wanted to state it, and we do ?genre= + returnBook.genre. Now if we come back over to our browser and we look at this individual item, first we have to actually return the book. Now if we come back over you'll see we have a link and that's not working quite right. We'll look at that here in just a second. Let's pull up a different book and then I'll show you why. If I click on this one I now have a link that says I can filter by this genre, so I click on that, and I get all the books that are fantasy books. Now the reason why that other one didn't work is it wasn't a clean URL. Notice how I have spaces here, and so when I click here it's not going to work right, so we can replace the space with a %20. Let's hope back over and do that real quick. This is a really simple fix, so we're just going to come over here and we'll get rid of that. We'll just do var newLink = that (Typing), this equals newLink.replace, it's just a string, so we're going to do a replace, replace spaces with %20s, we'll save that. Now basically all we're doing is we're creating the string, then we're replacing %20s where we need to, and adding that to the filter by this genre, so we'll save that, we'll pop back over to our browser, refresh, and now we have a working link. There you go.
This concludes our course on building RESTful APIs with Node and Express and we've covered a lot of stuff in a pretty short period of time, but ultimately we built a RESTful API using Node and Express and we used our book as our resource to get that done. We started with a conversation about what REST is, what it means, what the constraints are, and how you work through that. We talked about all of the REST verbs, get, put, post, patch, delete, what they all do, and when you would use them, and then we talked a little bit about unit and integration testing. We covered the surface of it, let you know how to do unit testing, how to build controllers, how to do integration testing using supertest to test everything, and then this last module we started talking about hypermedia and how you can embed links in your resources, so that the user and consumer of the API knows what other options they might have as they're navigating through your API to have this self documenting API that's easy and intuitive to navigate. Well I hope you enjoyed this course. I've enjoyed making it for you and use this as a great starting point to get an API up and running using Node.