GraphQL, described as a data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data, allows varying clients to use your API and query for just the data they need. It helps solve some performance issues that some REST services have — over-fetching and under-fetching. The GraphQL specification defines a type system, query language, and schema language for your Web API, and an execution algorithm for how a GraphQL service (or engine) should validate and execute queries against the GraphQL schema.
In this article, I'll focus on GraphQL subscription. It would be helpful if you already know about GraphQL query and mutation, which I have written about recently. This article will build on the sample application built from the previous articles on GraphQL mutation and query. If you want to code along, download the project from GitHub, and copy the files from src-part-2
folder to the main src
folder.
What Is GraphQL Subscription?
Almost every software application has some form of real-time in it. Take Uber for example. You want to be notified when your driver arrives. If it's a live score app, you want to see the scores and activity feed updated in real-time as the events happen.
The aim of GraphQL subscription is to help you build real-time functionality into your GraphQL applications, while still allowing only the data the client needs to be returned from events as they happen. The way this happens is that the client establishes a bi-directional communication channel with the server by sending a subscription query that specifies which event it is interested in and what shape of data should be sent to it when the event is triggered. When the channel is established, the client or server can send data across the channel or close it. Events are anything the system cares about (e.g. new book created), and may contain a payload.
There's a subscription system that will handle incoming subscription operations. It will parse, validate, and store queries and variables sent by clients. If the subscription operation is valid, it'll map the combination of the root field and query variables to an event stream. When any event from the mapped event stream is triggered, the stored GraphQL query is executed, using the combination of saved variables, context, and event payload as input. The result from this execution is then sent to the client that subscribed to that event. The client can choose to unsubscribe at any time by sending a message to the server through the established communication channel, or the server may also choose to unsubscribe the client at any time due to errors, load, or timeouts.
Subscribe-Unsubscribe:
Publish scenario:
Implementing Subscriptions
Let's get on with implementing subscriptions. If you followed along from the previous articles, you should remember we installed an npm package graphql-yoga
. This package comes bundled up with some other dependencies to make it easier and faster to build a GraphQL backend. It comes with support for GraphQL subscription through two packages, graphql-subscriptions
and subscriptions-transport-ws
. If you have the project cloned following the instruction earlier, then it's also included.
We want clients to be notified when new books are created, so let's add a subscription operation to the GraphQL API to allow that. The first thing we do is to update our schema with the root subscription operation type, which will have a single root field. Open src/index.js
file, go to line 27 and add the code below to the schema.
type Subscription {
newBook: Book!
}
Then next we add the resolver for this operation and its root field. Go to line 34, where we defined the resolvers
variable, and edit it to include the resolver code below:
Subscription: {
newBook: {
subscribe: (parent, args, context, info) => {
return context.prisma.$subscribe
.book({ mutation_in: ["CREATED"] })
.node();
},
resolve: payload => {
return payload;
}
};
}
The way you implement the resolver for the fields in the subscription operation type is different than that of query and mutation. Rather than a function mapped to the field, it is mapped to an object with two properties, subscribe
and resolve
. The subscribe
property is mapped to a function that will return AsyncIterator
, which is used by the GraphQL server to push the event data to the client. This function has the same definition as the resolver function you defined for mutation and query operations. The resolve
property is what actually returns the data from what is emitted by the AsyncIterator
.
Prisma also makes it easy to build in subscription to your GraphQL API. It allows you to subscribe for events on the types in your data model. You can subscribe to three different types of events based on the types in your data model — created, updated, or deleted events. The code you just added subscribes to the created event for the Book model.
You can also filter a subscription event from the Prisma API to check if certain fields are updated or that a particular field contains certain values. Our requirement for our app is not a complicated one, so I won't go deep into that. However, you can read the documentation if you want to learn more about it.
Testing GraphQL Subscription
We have our server ready to respond to GraphQL subscription query. We will now test out the new feature we just added. Open the command line and run node src/index.js
to start the server. Open your browser to http://localhost:4000. In the playground editor, copy and run the subscription query below.
subscription {
newBook {
title
pages
chapters
authors {
name
}
}
}
This should initiate a long-lived connection between the server and the client. You won’t get any result immediately, unlike your mutation and query operations. The client will be waiting for data from the server, and when that comes, the client will display it.
Now let's trigger an event by running a mutation query to create a book. Click the plus icon in GraphQL playground which should open a new tab. Enter and run the mutation query below:
mutation{
book(title: "SVG for novice", pages: 290, chapters: 20,
authors: ["Sarah Jimoh", "Brimoh Aje"])
{
title
}
}
The mutation runs successfully and returns data. Switch back to the tab for the subscription. You'll notice it has the data we just created.
Let's update the schema such that while we can subscribe to a new book created event, we can also choose to only be notified when the title of the new book contains certain keywords.
Update the subscription type in the schema as follows:
type Subscription {
newBook(containsTitle: String): Book!
}
Then update the resolver as follows:
subscribe: (parent, args, context, info) => {
let filter = { mutation_in: ["CREATED"] };
if (args.containsTitle)
filter.node = { title_contains: args.containsTitle };
return context.prisma.$subscribe.book(filter).node();
},
To test this out, re-start the GraphQL. Run the following subscription query:
subscription {
newBook(containsTitle: "GraphQL") {
title
pages
chapters
authors {
name
}
}
}
Then open a separate tab to run mutation, which will create new books. Create two books: one whose title doesn't include the word GraphQL
, and another whose title does include GraphQL. You should notice you only see data in from the subscription query if the new book created has GraphQL
as part of its title.
What's Next
You've seen how GraphQL not only allows you to build APIs that have CRUD functionalities, but also real-time APIs using GraphQL subscriptions. The difference between subscriptions and queries or mutations lies in the execution. While queries and mutations follow a typical request-response pattern, subscriptions don’t return the requested data right away. Instead, when a GraphQL server receives a subscription request, it creates a long-lived connection to the client. I showed you how to build in subscription to your GraphQL server and we used some bindings from Prisma to make this work.
You can find the completed code on GitHub, in the directory src-part-3
. I hope you have enjoyed learning from this piece as much as I enjoyed writing it in order to educate you. If so, I urge you to stick around for the next one where I will show you some ways to handle authentication in a GraphQL server.
See you there! 🚀💛
Related
- Intro to GraphQL: Schema, Resolvers, Type System, Schema Language, and Query Language
- Intro to graphQL: GraphQL: Mutation and Database Access
Originally publish on Telerik Blog