The last topic that we’ll cover in this tutorial are GraphQL subscriptions. This section is all about bringing realtime functionality to your app using GraphQL subscriptions.
Subscriptions are a GraphQL feature allowing the server to send data to its clients when a specific event happens. Subscriptions are usually implemented with WebSockets, where the server holds a steady connection to the client. When working with subscriptions, you’re breaking the Request-Response-Cycle that was used for all previous interactions with the API.
With subscriptions, the client initiates a steady connection with the server by specifying which event it is interested in. Every time this particular event occurs, the server uses the connection to push the expected data to the client.
This method is really interesting to keep your app up-to-date without refreshing it! We’ll be using subscriptions to display new links and upvotes that other users are making (or that you’re making in another tab or window) while the app is running.
When using urql, you need to add the subscriptionExchange
to your Client, to tell it how to handle GraphQL subscriptions. We’ll use it together with the subscriptions-transport-ws
package, which exposes a SubscriptionClient
that establishes the WebSocket connection.
Go and add this dependency to your app first.
Let’s now add the subscriptionExchange
to your urql Client, which is part of the urql
package itself.
You’re instantiating a SubscriptionClient
that knows where to find the subscriptions endpoint. The subscriptions endpoint in this case is similar to the HTTP endpoint, except that it uses the ws
instead of the http
protocol. Notice that you’re also authenticating the websocket connection with the user’s token.
Lastly you’re adding the subscriptionExchange
to the Client’s config and are passing it a forwardSubscription
handler that passes the subscription operation on to your SubscriptionClient
.
For the app to update in realtime when new votes are added to links, you need to subscribe to events that are happening on the Link
type. This means that you’ll be writing a subscription definition, which is very similar to a query.
This looks very similar to the VoteMutation
definition that you’ve written earlier. But instead of sending a mutation or query, you’re subscribing to any new votes and are asking for the updated votes
field on the event’s link.
Now the only thing you’ll need to do is add the subscription to the LinkList
component.
This is all that you need to add to subscribe to new votes! You also don’t have to write a new updater function for Graphcache, like you had to for the post
mutation, because the normalized cache can simply update the link that the subscription definition asks for.
The useSubscription
hook is actually very similar to useQuery
and useMutation
. If you’d be using a non-normalized document cache, you could look at each event’s result and manually reconcile it with a query’s result. In this case however, the normalized cache can take care of all updates for us! So you only have to add the hook and don’t have to worry about anything else! 😍
Let’s finally add some subscriptions! You’ll add one that automatically displays new links in the LinkList
as they’re posted by other users!
We’ll again write a new subscription definition and add another useSubscription
hook.
Unfortunately in this case, like with the post
mutation, new links won’t automatically be added to the currently displayed LinkList
. But you can easily fix this by writing another updater function!
This function is essentially identical to the updates.Mutation.post
updater function that you’ve written previously. The only difference is the naming of the property on the result. In this case newLink
is added to the FEED_QUERY
data instead of post
.
And that’s it! Your app is now ready for realtime and will immediately update links and votes whenevert they’re created by other users.
In several cases, it may not be possible for you to add subscriptions to your app. Maybe your GraphQL API doesn’t support them, or maybe you don’t have the infrastructure in place to host WebSocket servers.
We’d like to demonstrate a couple of methods that you can use to work around not having GraphQL subscriptions in this section.
Note: We won’t be changing any code in the tutorial’s app here, since we do have subscriptions for this tutorial, but feel free to try these tips and tricks out, if you’d like!
When you have a query you may want to programmatically refetch its content to update the cache. Your user may have an update button for this, or a pull-to-refresh gesture, or any number of ways.
Admittedly this isn’t the most elegant option, but in some cases it is just necessary to tell useQuery
to run another network request. You can do this by using the useQuery
hook’s executeQuery
function.
In the last section of this tutorial (“Pagination and Cache Updates”) we’ve mentioned the requestPolicy
option that useQuery
accepts. When you pass requestPolicy: 'cache-and-network'
to useQuery
, you can force it to send a network request, while also displaying cached data first.
As it turns out, executeQuery
also accepts the requestPolicy
option programmatically! You can use this to trigger a programmatic refetch:
const [result, executeQuery] = useQuery({
query: FEED_QUERY,
variables
})
const refetch = () => {
// refetch from network:
executeQuery({ requestPolicy: 'network-only' })
// refetch from network but keep displaying cached data:
executeQuery({ requestPolicy: 'cache-and-network' })
}
This will cause useQuery
to send another network request, which will also update all data in your normalized cache.
Another option for keeping your app’s data up-to-date is polling. With this method you’re refetching queries regularly within an interval.
You can add polling to any useQuery
hook by passing it the pollInterval
option. By doing this, every pollInterval
milliseconds useQuery
will refetch your query.
const [result, executeQuery] = useQuery({
query: FEED_QUERY,
variables,
// refetch ever 5seconds:
pollInterval: 5000,
// necessary so it updates from network:
requestPolicy: 'cache-and-network',
})
I hope you’ve enjoyed this last section of the tutorial, where you’ve learned how to add subscriptions to your app!