While polling for changes is a simple, safe solution, sometimes we'd like updates pushed to us by the server as soon as they happen. We might also like small chunks of updates, rather than re-requesting the whole initial query. In this lesson, we'll look at making GraphQL subscription with the "useSubscription" hook. We'll also set-up a conditional protocol link, that either uses the HTTP or Websocket protocols based on the type of query being attempted.
Instructor: [0:00] In the previous lesson, we looked at how to set up polling as a way to get live server updates for my categories. That only works if your data set is relatively small, as is the case with my categories. I can afford to fetch all of my categories every half-a-minute, for example, because I'll only have 3, 6, maximum 10 of them.
[0:21] Their text is only a few characters, but because I'm sharing this work space with others in my family, they might start adding some new notes. If I'm using the the app, I'd like to see in real time, what notes other people add.
[0:35] Currently, the only way for me to do that is to refresh the app, which is why you'll now see these two notes that just appeared at the top. They were just added by somebody else in the meantime. I can use the same strategy as before and set up polling for my notes list. Notes can have a lot of content, they can have pictures, they have lots of text. If I hadn't set up this load-more functionality, I could potentially have dozens of notes being downloaded each time we make a query.
[1:06] Let's see if there's a different way. I'll write some new JSX here. It's going to display the most recent note. I'll reuse my UI note component, which accepts a category and some content. Let me create a mock note for now with a category label and some content. I'll send its values to my new component.
[1:31] Finally, I'll add my new template above my notes list here. If I go to the browser, it looks like this now. We want that whenever somebody adds a new note while I'm using the app, we want this component to update to reflect that new note.
[1:50] We looked at how to make GraphQL queries using Apollo mutations. Now we're going to use the useSubscription hook to make a GraphQL subscription for a new shared note.
[2:02] The specific subscription we want to invoke is NewSharedNote, which will return a note as a type with the ID content and category with its ID and label. Same as my notes query, because we only want to display new notes for a specific category, this subscription will accept the category ID variable. I'll pass it to the NewSharedNote field as well.
[2:25] Finally, I'll need to pass it the variable category ID. Its value, I'll get it from my component props and I'll paste it here. Now this subscription will return me, same as a query, some data, but I'll get some red squiggly lines here. Identifier data has already been declared and it's been declared up here in my query.
[2:47] Let's use the JavaScript destructuring renaming syntax to rename it to New Note Data. Now that that's done, I can extract a new note from it, which will be whatever new note data my subscription gives me .newsharednote.
[3:03] It's this because remember, our data takes the shape of our query, which has a new shared note field. Now that I have a proper note, I can remove my mock. Let's only show my new note template if we actually have a note.
[3:18] Subscriptions are really useful for when you want to have the server push data to you as soon as it's available, rather than you having to pull and ask if it has updates every couple of seconds or minutes. You get the exact entity you asked for in the shape you wanted as soon as it's available.
[3:38] If I go back to my app and look at the network tab, we can see that the request has indeed been made to get a new shared note, but it's returned an error. The error itself is not important. The problem is that the server didn't know how to deal with this request.
[3:54] That's because it's been made as an HTTP post request. HTTP requests are good for requesting data and getting a response back. They cannot be used to open a persistent connection with the server so it can send us new data as it comes in, new notes, whenever new ones appear.
[4:11] It makes sense Apollo attempted to make an HTTP request when we gave it a subscription because the last link in our chain is currently an HTTP link. Let's import the WebSocketLink from the Apollo Client links, WS package. I will initialize it by pointing it to the URI of my GraphQL server.
[4:34] Notice how instead of HTTP, I'm using the WebSocket protocol. The WebSocket protocol offers a continuous connection to a given server, where the server can continuously push multiple items to the client, exactly what we need for our subscriptions and our use case.
[4:50] The problem is, if I directly add this to the end of my link chain, all requests will be using the WebSocket protocol now. If I put it before the HTTP link, then all requests will again be HTTP. All that matters is what is the last link in the chain?
[5:08] I'll import getMainDefinition from the Apollo Client utilities package and I'll also import the split utility from Apollo Client. Let's create a new protocol link and I'll use the split utility which accepts a function, which needs to return a Boolean, and then it accepts two other links. In my case, the WebSocketLink and the HTTP link.
[5:31] If this function returns true, it will use the first link. Otherwise, it's going to use the second. The split utility allows me to create a new link that conditionally chooses between two other links, depending on the condition I set in this function. The function accepts a query, so anytime any one of my components makes a GraphQL query, it's going to pass through this function.
[5:55] Then I can use the getMainDefinition utility I imported before to get my query's definition. A query's definition contains different details about the query. It also includes the operation it's attempting, such as a subscription, but it can also be a query or a mutation.
[6:14] In my case, if a component makes a query that contains a subscription, I want to use the WebSocketLink. Otherwise, use the HTTP link. Let me replace both my HTTP link and my WebSocketLink with my new conditional protocol link.
[6:30] If I go back to my browser, we'll see our familiar notes list, but after a few seconds, we'll see a new note appearing at the top under my recent changes template. After a few more seconds, another note. We're getting live updates pushed to us without us having to refresh the page.
[6:49] If we look in the network tab, we can see this request here, that's of type=WebSocket. If we open it up, you can inspect all the payloads sent to us by the server, including data for our NewSharedNotes.
[7:06] In summary, we used the useSubscription hook to make a new GraphQL subscription and get some new note data back. Subscriptions are a persistent type of GraphQL query where you can tell the server what data you want back, and it will push you that data whenever it changes on the back end.
[7:25] Whenever you get new data back, Apollo makes sure to rerender your component. To use subscriptions though, you first have to define a WebSocketLink that points to a WebSocket protocol URI, and then use the split utility to use the new WebSocketLink only for queries' operation and subscription. For everything else, use the HTTP protocol.
[7:50] Finally, you need to make sure that the new protocol link is the last one in your chain.