Introduction
Subscriptions are a very powerful feature of GraphQL. They allow you to have a real-time connection to your database, notifying and updating your data when there are changes. Subscriptions have tons of applications, such as real-time chat applications or live comment feeds on articles.
In this tutorial, we are going to create a minimal real-time chat application using GraphQL Nexus and React Apollo. Hopefully by the end of this, you’ll be a pro at using subscriptions.
Meat and Potatoes
Getting Set Up
To get started, download this repository: https://github.com/hkyang995/graphql-nexus-subscription-starter-backend
This project contains a schema with a single
Post
type- Post
has two fields, author
and content
. We will set up a subscription to update a live chat feed with every new post that is made.
If you take a peek at src/schema.ts
, you’ll see two queries,
post
and posts
. The post
query returns the single most recent post, while posts
returns every post in the database. And as you might have guessed, the createPost
mutation creates a post.
Let’s get started by installing our tools: GraphQL Yoga, GraphQL Nexus, and GraphQL. We’ll be using Prisma’s demo servers to help get things set up and conveniently host all of our information. The starter file uses yarn to tie our dependencies together, so all we need to do is:
yarn
To start the server at any time during the tutorial, use:
yarn dev
Now that we’ve installed everything, we can create a server with Prisma using:
prisma init
This command will walk us through the creation of the server. Feel free to choose whatever suits your needs, but for simplicity’s sake, these options will do just fine:
- Demo server
- Choose EU or US
- Name your shiny new service
- Choose a name for this stage (just the default is fine)
- Choose Typescript for our language of choice
Your server will be good to go after running prisma generate
.
Now we’re finally ready to dive into making our Subscription!
Creating the Subscription on the Backend
Now that we are set up, we are ready to create our Subscription. Since each Subscription needs to return a payload (the bundle of information sent back to you), we’ll add a payload type to our schema.
const PostSubscriptionPayload = objectType({
name: "PostSubscriptionPayload",
definition(t) {
t.field("node", {
type: Post,
nullable: true
});
t.list.string("updatedFields", { nullable: true });
}
});
Like mentioned above, this payload type is the object type that will be returned from our subscription. The key item that we’re going to be looking at is t.field(“node”)
. We set its type to Post
so it’ll return exactly what we need, a Post
!
const messageSubscription = subscriptionField("post", {
type: PostSubscriptionPayload,
subscribe: (root, args, context) => {
return context.prisma.$subscribe.post({ mutation_in: "CREATED" }) as any;
},
resolve: payload => {
return payload;
}
});
Here’s the function that’s going to be doing most of the work. You might be thinking, “That’s it??” and yes, that is it! You don’t need anything else on the backend for this particular application.
Here’s how this code works. We set the type to PostSubscriptionPayload
to return our post. You can see that we pass in an argument to the post mutation_in: ‘CREATED’
, which means that we’re only going to subscribe to newly created posts (as opposed to posts that are edited or deleted). Finally, we return the payload which completes our Subscription!
You can test this out on your GraphQL Playground by starting it up with yarn dev
. When you run the Subscription, it will start listening for new posts. When you create a new post using the createPost
mutation, you’ll be able to see it in your Subscription’s tab.
You can check out and download the completed backend code here:
https://github.com/hkyang995/graphql-nexus-subscription-starter-backend/tree/completed
Creating the Subscription on the Frontend
We have our subscriptions working on the backend, but we’re not out of the woods yet. The next steps are to make subscriptions work on the frontend so we can see our shiny new Posts
in real time.
To start out, let’s set up a simple UI and connect our frontend to the backend. To get started, download this repo of frontend code:
https://github.com/hkyang995/graphql-nexus-subscription-starter-frontend
To run the application at any time, use yarn start
in the command line on the frontend folder.
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/`,
options: {
reconnect: true
}
});
const httpLink = createHttpLink({
uri: "http://localhost:4000/"
});
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscription";
},
wsLink,
httpLink
);
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
If you take a look at src/App.js
, you’ll see that we’re using Apollo to connect our frontend with our backend. The backend server is set to localhost:4000
, which can be changed if your server is being hosted elsewhere. We’re also connecting a WebSocket to all of this so we are able to get our subscriptions in real time.
Most of the legwork is being done in our components function, src/AppContents.js
. In this file, there is a function that takes the input and does a mutation to push the Post to our server. In src/ChatBox.js
, we query for the Posts
that already exist and display them to the user.
For now, we can write out messages and submit them, but the chat box won’t update unless we refresh. To fix this, we will set up our Subscription on the frontend.
Using one of our imported packages, graphql-tag
(gql
), we can set up a subscription on the frontend like this:
const NEW_POST_SUBSCRIPTION = gql`
subscription PostSubscription {
post {
node {
content
id
author
}
}
}
`;
Since we defined our subscription on the backend, we only need to specify what we want to grab from it on the frontend. Here we’re getting the content, id, and author.
<Query query={GET_EXISTING_POSTS}>
{({ subscribeToMore, loading, error, data }) => {
The subscribeToMore
function comes packaged in Apollo GraphQL, and is going to be our best friend on the frontend, as it’s going to get our Subscriptions to work. We can pass it through in our query function.
<ChatView
data={data}
subscribeToMore={() =>
subscribeToMore({
document: NEW_POST_SUBSCRIPTION,
updateQuery: (prev, { subscriptionData }) => {
if (!subscriptionData.data) return prev;
const { node } = subscriptionData.data.post;
return Object.assign({}, prev, {
posts: [...prev.posts, node]
});
}
})
}
/>
Here, we’re passing the subscribeToMore
function into our ChatView
component. Let’s break down how this all works.
We’re passing the subscription into the document
field, and updateQuery
is a function that runs every time our query is updated.
const { node } = subscriptionData.data.post;
We can pull out the node from the subscription data, which contains all of the information about the post: the content, the post id, and the author of the post.
return Object.assign({}, prev, {
posts: [...prev.posts, node]
});
At the very end, we’re updating our posts
by setting it equal to its previous values, along with the new node we got from the subscription.
componentDidMount() {
this.props.subscribeToMore();
}
The last thing we need to do is add the subscribeToMore
function into the ChatView
component’s componentDidMount
function. This will allow it to update whenever it needs to.
And there you have it! Now whenever a message is sent, your subscription will update the frontend.
The completed code can be found here.
https://github.com/hkyang995/graphql-nexus-subscription-starter-frontend/tree/completed
Conclusion
In this tutorial, we built a real-time chat application using GraphQL subscriptions. With this under your belt, subscriptions will appear less daunting for more complex applications.
If you have any questions, comments, concerns, or just want to tell me about your day, feel free to leave a comment. For more content like this, feel free to follow Novvum on Twitter. Thank you!
Top comments (0)