In this section, you’ll learn how you can bring realtime functionality into your app by implementing GraphQL subscriptions. The goal is to implement two subscriptions to be exposed by your GraphQL server:
Link
element is createdLink
element is upvotedSubscriptions are a GraphQL feature that allows a server to send data to its clients when a specific event happens. Subscriptions are usually implemented with WebSockets. In that setup, the server maintains a steady connection to its subscribed client. This also breaks the “Request-Response-Cycle” that were used for all previous interactions with the API.
Instead, the client initially opens up a long-lived connection to the server by sending a subscription query that specifies which event it is interested in. Every time this particular event happens, the server uses the connection to push the event data to the subscribed client(s).
We will be using PubSub
from the apollo-server
library that we have already been using for our GraphQL server to implement subscriptions to the following events:
You will do this by first adding an instance of PubSub
to the context, just as we did with PrismaClient
, and then calling its methods in the resolvers that handle each of the
above events.
PubSub
Here, you’re creating an instance of PubSub
and storing it in the variable pubsub
, just as we stored an instance of PrismaClient
in the variable prisma
.
Great! Now we can access the methods we need to implement our subscriptions from inside our resolvers via context.pubsub
!
Link
elementsAlright – let’s go ahead and implement the subscription that allows your clients to subscribe to newly created Link
elements.
Just like with queries and mutations, the first step to implement a subscription is to extend your GraphQL schema definition.
Next, go ahead and implement the resolver for the newLink
field. Resolvers for subscriptions are slightly different than the ones for queries and mutations:
AsyncIterator
which subsequently is used by the GraphQL server to push the event data to the client.subscribe
field. You also need to provide another field called resolve
that
actually returns the data from the data emitted by the AsyncIterator
.The code seems pretty straightforward. As mentioned before, the subscription resolver is provided as the value for a subscribe
field inside a plain JavaScript object.
Now you can see how we access pubsub
on the context
and call asyncIterator()
passing the string "NEW_LINK"
into it. This function is used to resolve subscriptions and push
the event data.
The last thing we need to do for our subscription implementation itself is to actually call this function inside of a resolver!
Now you can see how we pass the same string to the publish
method as you added in your newLinkSubscribe
function just above, along with passing in the newLink
as a second
argument!
Ok, I’m sure you’re dying to test out your brand-spanking new Subscription! All we need to do now is make sure your GraphQL server knows about your changes.
With all the code in place, it’s time to test your realtime API ⚡️ You can do so by using two instances (i.e. tabs or windows) of the GraphQL Playground at once.
As you might guess, you’ll use one Playground to send a subscription query and thereby create a permanent websocket connection to the server. The second one will be used to send a
post
mutation which triggers the subscription.
In contrast to what happens when sending queries and mutations, you’ll not immediately see the result of the operation. Instead, there’s a loading spinner indicating that it’s waiting for an event to happen.
Time to trigger a subscription event.
Now observe the Playground where the subscription was running:
vote
mutationThe next feature to be added is a voting feature which lets users upvote certain links. The very first step here is to extend your Prisma data model to represent votes in the database.
As you can see, you added a new Vote
type to the data model. It has one-to-many relationships to the User
and the Link
type.
To apply the changes and update your Prisma Client API so it exposes CRUD queries for the new Vote
model, regenerate PrismaClient
.
Now, with the process of schema-driven development in mind, go ahead and extend the schema definition of your application schema so that your GraphQL server also exposes a vote
mutation:
type Mutation {
post(url: String!, description: String!): Link!
signup(email: String!, password: String!, name: String!): AuthPayload
login(email: String!, password: String!): AuthPayload
vote(linkId: ID!): Vote
}
It should also be possible to query all the votes
from a Link
, so you need to adjust the Link
type in schema.graphql
as well.
You know what’s next: Implementing the corresponding resolver functions.
Here is what’s going on:
post
resolver, the first step is to validate the incoming JWT with the getUserId
helper function. If it’s valid, the function will return
the userId
of the User
who is making the request. If the JWT is not valid, the function will throw an exception.linkId
and userId
. If the vote exists, it will be stored in the vote
variable, resulting in the boolean true
from your call to Boolean(vote)
—
throwing an error kindly telling the user that they already voted.Boolean(vote)
call returns false
, the vote.create
method will be used to create a new Vote
that’s connected to the User
and the Link
.You also need to account for the new relations in your GraphQL schema:
votes
on Link
user
on Vote
link
on Vote
Similar to before, you need to implement resolvers for these.
Finally you need to resolve the relations from the Vote
type.
Great job!
Finally the Vote
resolver needs to be included in the main resolvers
object in index.js
.
Your GraphQL API now accepts vote
mutations! 👏
The last task in this chapter is to add a subscription that fires when new Vote
s are being created. You’ll use an analogous approach as for the newLink
query for that.
Next, you need to add the subscription resolver function.
All right, that’s it! You can now test the implementation of your newVote
subscription!
You can use the following subscription for that:
subscription {
newVote {
id
link {
url
description
}
user {
name
email
}
}
}
If you’re unsure about writing one yourself, here’s a sample vote
mutation you can use. You’ll need to replace the __LINK_ID__
placeholder with the id
of an actual Link
from your database. Also, make sure that you’re authenticated when sending the mutation.
mutation {
vote(linkId: "__LINK_ID__") {
link {
url
description
}
user {
name
email
}
}
}