Subscriptions in GraphQL with Apollo 2.0

In this article we will create a small application to create real time messages and update the favorite ones by using subscriptions with GraphQL, Apollo Server, Apollo Client and React.

Subscriptions with GraphQL and Apollo 2.0

What are subscriptions and where to use them?

Subscriptions are GraphQL operation­­s to watch events emitted by a server via events. Subscriptions use WebSockets, that means there is an open connection between the server and the client where information is passed back and forth without a specific request-response.

You might want to add subscriptions to your application if you need real time information. In order to achieve that, you need to open listeners in the back-end to specific events in the front-end.

Subscriptions

GraphQL Subscriptions in the back-end with Apollo-Server 2.0

In this blog post we will focus in configuring and implementing subscriptions; however, we need to mention that we are using sequelizer as our ORM (Object-Relational Mapping) to talk to the database on MySql, you can see the configuration of that part of the backend in the itr-apollo-server GitHub repo.

Packages

Firstly, we need to install the necessary packages:

  • apollo-server
  • apollo-server-express
  • express
  • graphql
  • graphql-tools
npm install — save apollo-server apollo-server-express express graphql graphql-tools

Schema

Secondly we need to configure our Apollo server to listen to subscriptions by defining our models, queries, mutations and subscriptions.

Subscriptions are another root level type so they need to be defined in our schema with a name and a return type similar to the mutations and queries.

GraphQL schema.

Resolvers

After defining our schema, we need to set our resolvers. In order to configure the subscriptions we require PubSub which comes from the terms Publish and Subscribe; its purpose is to create event generators.

Note: PubSub comes out of the box in Apollo Server for demo purposes; however, it is advisable a replacement by any other implementation of PubSub engine interface in order to scale in production.

In this example we are adding a subscription when we create and update a message. Notice that Subscription resolvers are objects (not functions) with a Subscribe method that returns an AsyncIterable to map with the desired event.

GraphQL resolvers

Publish

In our createMessage mutation we are adding an extra step after the message is created; that step is where we are publishing a MESSAGE_CREATED event and sending an object of type Message.

Mutation to create a message and adding a subscription

In a similar way we publish a MESSAGE_UPDATED on the updateMessage mutation.

Mutation to update a message and adding a subscription

Those steps are important because we are telling the server to trigger certain event and the front end will be listening for those events, in this case: MESSAGE_CREATED and MESSAGE_UPDATED.

Subscribe

In the Subscription section of our resolver we will subscribe to the event we need via the asyncIterator. For example, messageCreated subscription:

Subscription when a message is created

For the messageUpdated subscription we will apply a filter in order to control the publication for a specific message. In this case we just want to publish the message that was updated based on the id.

Subscription when a message is updated

Notice that when using withFilter we do not need to wrap the return with a function.

Applying middleware to our server

The last step on the server side is to use installSubscriptionHandlers as a middleware to our Apollo Server in order to use subscriptions.

GraphQL Apollo Server 2.0

Before we move on to the front end, it is a good practice to test that our mutations and subscriptions are running as expected in the back-end, that will help us to avoid errors further along our implementation on the front-end.

Testing subscriptions

When running subscriptions on the playground we should just see the play button changing from Play to Stop; this means we opened a listener, just when a subscription is triggered, a result will be displayed, otherwise do not expect to see anything.

Testing subscriptions. (Notice the Listening…)

Testing mutations

Testing Mutations

You can find the source code for the back-end in the following GitHub repo: itr-apollo-server. Now let’s move on to the front-end implementation.


GraphQL Subscriptions in the front-end with Apollo-Client 2.0

We will create the already well known create-react-app, add some components and styles to set our basic UI. You can see that boilerplate in the itr-apollo-client GitHub repo.

WebSocketLink

Focusing on the subscriptions, the first step is to add the WebSocketLink via the ApolloLink middleware, so we need to install:

  • apollo-link
  • apollo-link-ws
npm install — save apollo-link apollo-link-ws

The purpose of those packages is to provide a way to direct to different links depending on the operation, so make sure you add the corresponding imports in your index.js file on your front-end application. We also need to add the ApolloProvider and wrap our App on it.

Your index.js file should look like the following:

Wrapping our React app in the Apollo Provider

Usually a normal HttpLink is enough to set the Apollo client; however, as we need to get access to the WebSocket we need to create an instance of a WebSocketLink so we know the endpoint.

Directional Composition

Split allows to control the flow of the operations; for example if it is a mutation it will go via the HttpLink and if it is a subscription it will go to via the WebSocketLink; this is called Directional Composition.

Directional Composition using Split

Once we have our Apollo provider configured let’s implement our two subscriptions: messageCreated and messageUpdated in our components.

Components flow

We will create a component (message) where the messages will be created. For simplicity, I will focus on the two most important elements: the graphql-tag and the trigger event.

Creating a message in our react app

Now, let’s create a Query component that retrieves the list of messages, the render prop function passed to the query component has among some other properties, the subscribeToMore which is a function that we will use to set up the subscription, hence we will pass it as a property to our list of messages.

First we need:

npm install — save react-apollo
Query to get a list of messages

As far as the graphql query to retrieve the list of messages, I recommend you to copy the same query you had in the playground on the back-end testing section.

messageCreated

We will add our first subscription (messageCreated) in the componentDidMount method of our list of messages component.

We need to pass the following properties:

  • document, which is the name of our document passed by the graphql-tag (MESSAGE_CREATED)
  • updateQuery, which is the function that will be executed once we are notified that an event happened. In this case we will add the message created (subscriptionData.data.messageCreated) at the top of our current list of messages (…prev.allMessages).
Subscribing to a Message Created

messageUpdated

In order to you show another way to create a subscription we are going to use the <Subscription /> component from the react-apollo package that we already installed. The first step is to create the graphql tag, once again remember to use the code you used to test your back-end in order to avoid errors.

The second step is to add the subscription component in our render method and pass the following properties:

  • subscription, which is the name of our graphql tag
  • variables (if any)

In the children section we can render some specific component or information based on the response of the subscription or simply return null.

Render method using a subscription component.

Notice that we can nest subscriptions inside a query or a mutation; however, this could cause performance issues as the component gets updated and re-rendered every time.

Our application is ready to emit events and trigger subscriptions; make sure your server is running ( in this case http://localhost:4000/ ) and open your browser on http://localhost:3000/. If you add a message, the list should add it automatically at the top and if you favorite one message and open two tabs, the change should be reflected on real time in both tabs.