You might still have some REST API endpoints that haven't been converted to GraphQL yet. In this lesson, we'll look at adding the RestLink. This allows us to add annotation to our GraphQL queries that tell Apollo to convert and re-direct to a REST endpoint behind the scenes. And since REST has no concept of types, we'll look at how to annotate types as well. This allows us to continue using a single way of querying for data in our components, without mixing fetch REST calls and GraphQL queries. It also provides a smooth upgrade path, since we can just remove the annotations once the conversion to GraphQL is finished on the backend.
Instructor: [0:00] Up until now, we've been assuming that you'll have a full GraphQL server available to power all of your queries. That's not always the case. What if my notes query wasn't available on the GraphQL side yet? Our backend team is still migrating from a REST API to GraphQL, and they haven't offered the notes query yet.
[0:21] What if instead, my API, which lives on localhost port 4000, had a REST endpoint where I could make a GET call for the notes, and I could pass them the category ID, offset, and limit as query params. This would just return me the notes that I need in JSON format.
[0:42] To use this API, I could always just make a fetch call to get the data. Then, to use it in my React components, I probably need to wrap it in useEffect maybe, assign the response to some local piece of state. I just end up with a lot of different ways of grabbing data in my components.
[0:58] I'd have maybe some GraphQL queries, but then I'd also have these fetch calls. It would be that much more difficult to refactor everything back to GraphQL queries once the backend implements this endpoint.
[1:11] Let me open up the terminal and I'll install the apollo-link-rest package. Back in my top level index.js file, I'll import the REST link from the apollo-link-rest package. I'll initialize it down here by passing it the URI of my REST API.
[1:31] Now I'll just need to add it to my chain of links down here. The HTTP link still needs to be the last link as it's the one actually reaching out to a server via HTTP calls. I can just drop my REST link somewhere between these two.
[1:48] Back in my NotesList component, I can uncomment this and I'll annotate my notes query with @REST and I'll pass it the path to where it can find my list of notes on the REST server. We already declared this part when we instantiated our REST link, so we can just remove it.
[2:10] These are now static. To retrieve the actual passed in argument, I'll just replace it with args.category ID. That will make it replace this value with whatever is passed in here. I'll do the same for offset and limit. Cool. Let's try this out in the browser. Couldn't load notes.
[2:30] If we look in the network tab, we can see our retry link try to make a call for notes a few times, but they all failed. If I click on them, they're indeed using the REST API endpoint that I defined with the correct category ID, offset, and limit of three.
[2:49] Our changes worked and the response was successful. If we look at the JSON response, all my notes look fine. What gives? Well, let's open up one of our GraphQL calls, the one that gives us the categories.
[3:05] If we look at the response, we can see that each category is associated with a type name. This is added by the backend based on the GraphQL schema. Our REST API has no idea about schemas and types. It just gives us a list of notes in JSON format.
[3:26] To tell the REST link what type of data you expect to return from your query, we need to pass it the type option, and I'll set it to capital NOTE. If we go back to the browser, we can see it worked.
[3:40] We're actually running in full hybrid mode with some queries, like categories, going to our GraphQL server and some others, like the one for the list of notes, actually going to our REST API. What about mutations?
[3:55] Well, I can delete a note by making an HTTP delete call to my REST server @/notes/the ID of the note I want to remove. I'll annotate my mutation and pass it the path of the delete endpoint. I'll use args.ID to grab the ID of the note that I want to delete.
[4:16] I can also optionally pass it the HTTP method I want to use. In this case, DELETE. Finally, it's good practice to give a type to the response as well. Let's assume we're not doing optimistic responses. Let me just log the deleted note ID here to make sure everything works OK.
[4:36] If I go to the browser, I'll try to delete a note. I can see the request fired. It's going to our REST API @/notes/the ID of the first note. It's using the DELETE method. On the response, we can see it successful and we actually get the ID of the deleted note back.
[4:57] If I look at my console, I can see that the calculated deleted note ID was undefined. Well, that's because we added a type to our query response, but our note still doesn't have a type. Remember, it's just coming back from a REST endpoint which has no idea about types.
[5:16] Let me annotate it with @Type and I'll pass it the name note. If I go back here and I tried to delete the first note, we can see that now it's properly calculating its ID. The note also got removed from the UI.
[5:32] In summary, we used the apollo-link-rest package to create a new link pointing to our REST API. We added it to our links chain, ensuring the HTTP link stays last. Then, we annotated our queries with @REST and told it what type we expect back from it, and also, where in the API it can find a response.
[5:59] The REST annotation also allows us to optionally define an HTTP method, such as DELETE. We can even define the types of specific sub-fields, such as this note here. Since we're just using annotations, once this mutation is fully available on the GraphQL side, to convert our components, it's as easy as just removing these annotations. Now we're back to GraphQL mode.