March 17, 2017
How to build a Real-Time Chat with GraphQL Subscriptions and Apollo 🌍
In this tutorial, we explain how to build a chat application where the users can see their own and the locations of the other participants on a map. Not only the chat, but also the locations on the map get updated in realtime using GraphQL subscriptions.
⚠️ This tutorial is outdated! Check out the Prisma examples to learn how to build GraphQL servers with a database. ⚠️
In this tutorial, we explain how to build a chat application where the users can see their own and the locations of the other participants on a map. Not only the chat, but also the locations on the map get updated in realtime using GraphQL subscriptions.
Note: If you’re just getting started with GraphQL, check out the How to GraphQL fullstack tutorial for a holistic and in-depth learning experience.
What are GraphQL Subscriptions?
Subscriptions are a GraphQL feature that allow to get realtime updates from the database in a GraphQL backend. You set them up by subscribing to changes that are caused by specific mutations and then execute some code in your application to react to that change.
Using the Apollo client, you can benefit from the full power of subscriptions. Apollo implements subscriptions based on web sockets.
The simplest way to get started with a subscription is to specify a callback function where the modified data from the backend is provided as an argument. In a fully-fledged chat application where you’re interested in any changes on the Message type, which is either that a new message has been sent, that an existing message was modified or an existing message was deleted this could look as follows:
Note: This code assumes that you have configured and set up the
ApolloClient
and made it available in the props of your React component usingwithApollo
. We'll explain how to setup theApolloClient
in just a bit.
Figuring out the Mutation Type
The kind of change that happened in the database is reflected by the mutation
field in the payload which contains either of three values:
CREATED
: for a node that was addedUPDATED
: for a node that was updatedDELETED
: for a node that was deleted
Getting Information about the changed Node
The node field in the payload allows us to retrieve information about the modified node. It is also possible to ask for the state that node had before the mutation, you can do so by including the previousValues
field in the selection set:
Now you could compare the fields in your code like so:
If you specify previousValues
for a CREATED
mutation, this field will just be null
. Likewise, the node
for a DELETED
mutation will be null
as well.
Subscriptions with Apollo
Apollo uses the concept of an Observable
(which you might be familiar with if you have worked with RxJS before) in order to deliver updates to your application.
Rather than using the updated data manually in a callback though, you can benefit from further Apollo features that conventiently allow you to update the local ApolloStore
. We used this technique in our example Worldchat app and will explain how it works in the following sections.
Setting up your Graphcool backend
Note: Graphcool has been replaced by Prisma.
First, we need to configure our backend. In order to do so, you can use the following data model file that represents the data model for our application:
Since the data model file is already included in this repository, all you have to do is download or clone the project and then use our CLI tool to create your project along with the specified schema:
This will automatically create a project called Worldchat
that you can now access in our console.
Setting up the Apollo Client to use Subscriptions
To get started with subscriptions in the app, we need to configure our instance of the ApolloClient
accordingly. In addition to the GraphQL endpoint, we also need to provide a SubscriptionClient
that handles the websocket connection between our app and the server. To find out more about how the SubscriptionClient
works, you can visit the repository where it's implemented.
To use the websocket client in your application, you first need to add it as a dependency:
Once you’ve installed the package, you can instantiate the SubscriptionClient
and the ApolloClient
as follows:
Note: You can get the your PROJECT ID directly from our console. Select your project and navigate to
Settings -> General
.
Now, as usual, you will have to pass the ApolloClient
as a prop to the ApolloProvider
and wrap all components that you'd like to access the data that is managed by Apollo. In the case of our chat, this step looks as follows:
Building a Real-Time Chat with Subscriptions 💬
Let’s now look at how we implemented the chat feature in our application. You can refer to the actual implementation whenever you like.
All we need for the chat functionality is one query to retrieve all messages from the database and one mutation that allows us to create a new message:
When exporting the component, we’re making these two operations available to our component by wrapping them around it using Apollo’s higher-order compoment graphql
:
We then subscribe for changes on the Message
type, filtering for mutations of type CREATED
.
Note: Generally, a mutation can take one of three forms:
CREATED
,UPDATED
orDELETED
. The subscription API allows to use afilter
to specify which of these you'd like to subscribe to. If you don't specify afilter
, you'll subscribe to all of them by default. It is also possible to filter for more complex changes, e.g. forUPDATED
mutations, you could only subscribe to changes that happen on a specific field.*
Notice that we’re using a different method to subscribe to the changes compared the first example where we used subscribe
directly on an instance of the ApolloClient
. This time, we're calling subscribeToMore
on the allMessagesQuery
(which is available in the props of our compoment because we wrapped it with graphql
before).
Next to the actual subscription that we’re passing as the document
argument to subscribeToMore
, we're also passing a function for the updateQuery
parameter. This function follows the same principle as a Redux reducer and allows us to conveniently merge the changes that are delivered by the subscription into the local ApolloStore
. It takes in the previousState
which is the the former query result of our allMessagesQueryand
the subscriptionData
which contains the payload that we specified in our subscription, in our case that's the node
that carries information about the new message.
From the Apollo docs:
subscribeToMore
is a convenient way to update the result of a single query with a subscription. TheupdateQuery
function passed tosubscribeToMoreruns
every time a new subscription result arrives, and it's responsible for updating the query result.
Fantastic, this is all we need in order for our chat to update in realtime! 🚀
Adding Geo-Locations to the App 🗺
Let’s now look at how to add a geo-location feature to the app so that we can display the chat participants on a map. The full implementation is located here.
At first, we need one query that we use to initially retrieve all locations and their associated travellers.
Then we’ll use two different mutations. The first one is a nested mutation that allows us to initially create a Location
along with a Traveller
, rather than having to do this in two different requests:
We also have a simpler mutation that will be fired whenever a traveller logs back in to the system and we update their location:
Like before, we’re wrapping our component before exporting it using graphql
:
Finally, we need to subscribe to the changes on the Location
type. Every time a new traveller and location are created or an existing traveller updates their location, we want to reflect this on the map.
However, in the second case when an existing traveller logs back in, we actually only want to receive a notification if their location is different from before, that is either latitude
or longitude
or both have to be changed through the mutation. We'll include this requirement in the subscription using a filter again:
Let’s try to understand the filter
step by step. We want to get notified in either of two cases:
- A new location was
CREATED
, the condidition that we specified for this is simply:mutation_in: [CREATED]
- An existing location was
UPDATED
, however, there must have been a change in thelatitude
and/orlongitude
fields. We express this as follows:
We’re then putting these two cases together connecting them with an OR:
Now, we only need to specify what should happen with the data that we receive through the subscription — we can do so using the updateQueries
argument of subscribeToMoreagain
:
In both cases, we’re simply incorporating the changes that we received from the subscription and specify how they should be merged into the ApolloStore
. In the CREATED
-case, we just append the new location to the existing list of locations. In the UPDATED
-case, we replace the old version of that location in the ApolloStore
.
Summing Up
In this tutorial, we’ve only scratched the surface of what you can do with our subscription API. To see what else is possible, you can check out our documentation.
Don’t miss the next post!
Sign up for the Prisma Newsletter