Subscriptions with GraphQL

First of, I found this a time consuming task just to figure out where to start, how to test this functionality and whehter I should switch to use Apollo instead, as there are many resource out there that show how subscriptions work well with their approach, not to forget the Apollo graphiql tool allows you to test subscriptions.
After a lot of searching, many articles read and videos watched, I kept coming back to GitHub’s graphql/express-graphql
issues page where several people, dating back to August 2017 have been wanting to implement subscriptions.
As my project is build using express
and express-graphql
I wanted to make it work and not give in to Apollo 😁
My project uses graphql-ws
from enisdenjo/graphql-ws — an amazing repo and contains everything you need to get it working for front and back end solutions.
I also recommend reading Denis Badurina’s blog post on GraphQL over WebSockets, it’s a great read.
Throughout this article, I’ll share some snippet’s of my project for each part affected by subscriptions.
Let’s start with the server setup.
GraphQL Server
Make sure you have graphql-ws
, ws
and graphql-subscriptions
added to you package.json file.
npm install graphql-ws ws graphql-subscriptions
In the snippet below, we have our standard express-graphql
server with the WebSockets server running of express.
TypeDefs
Our schema file user.types.js
contains our User schema definition. I’ve added a subscription operation of newUser
, this will let us know when a new user has been created using the createUser
mutation.
type Query {
login(input: UserLogin!): String
users: [User]
user(id: ID!): User
} type Mutation {
createUser(input: UserMutation): User
updateUser(input: UserMutation): User
deleteUser: String
} type Subscription {
newUser: User!
}type User {
...
Resolver
Next, the user.resolver.js
this is where we hook up the event to the subscription using PubSub (Publish/Subscribe pattern).
I’ve created a helper module to import the PubSub instance.
const { PubSub } = require('graphql-subscriptions')
const pubsub = new PubSub()module.exports = pubsub
PubSub
PubSub
is a class that exposes a simplepublish
andsubscribe
API.It sits between your application’s logic and the GraphQL subscriptions engine — it receives a publish command from your app logic and pushes it to your GraphQL execution engine. Apollo documentation
NOTE that the default PubSub implementation is intended for demo purposes. It only works if you have a single instance of your server and doesn’t scale beyond a couple of connections. For production usage you’ll want to use one of the PubSub implementations backed by an external store. (e.g. Redis)
[from graphql-subscriptions — npm]
Continuing…
The pubsub.publish
function happens inside the createUser
function and has 2 arguments,
1.triggerName
— A string identifier
2.payload
— An object with the subscription type newUser
, as defined in the user.types.js
schema definition, along with the data to return.
The Subscription property on the resolver is similarly written like Queries and Mutations but notice it has a subscribe
method that returns AsyncIterable
, with the same triggerName
value as the publish function.
How does it work?
The client listens (subscibes) to the newUser
and waits for the NEW_USER_EVENT
to be triggered.
As soon as createUser
is called, it says, if anyone listening with NEW_USER_EVENT
, I’m going to send (publish) this newUser
object with the data.
Now that the server has been setup, it’s time to setup the client.
Client:- Angular & RxJS
As mentioned earlier, the graphql-ws
GitHub page has many examples of how to implement its package for server or client. As I’m a big Angular fan and one of the example used, was using Observables, I will be Angular and RxJS to connect to our subscription.
If you are not familiar with Angular or RxJS please check the recipes for graphql-ws, if you are, great, let’s go.
These are the additional packages required for the Angular project.
npm i graphql graphql-ws graphql-request
graphql-request is a GraphQL client supporting Node and browsers for scripts or simple apps and also recommended on graphql.org.
Even though it’s not used with subscripions, it’s perfect for queries and mutations, and I’ll leave it in the snippets for reference.
To keep it tidy and stuff it all into one file, we’ll create a service for the GraphQL server calls, this way we keep the app DRY and another service for our queries.
GraphQL Service
You can see there is a fetch
method for queries and mutations, this returns an observable but by default returns a Promise.
The subscription
method for… subscriptions. NgZone is used inside our subscription to update client windows.
User Service
Next, is the user service, here we write our queries to be consumed by our components. createUser
is the mutation query which returns the firstName and email
and the newUserEvent
is the subscription which returns the id
of the created user.
App Component
In our app.component.ts
file we’ve created an observable called createUserEvent$
which will return the newUser data and log it in the console.
The createUser
function sends the mutation, triggers the subscription and returns the createUser data to the console.
You can now call createUser()
in the view (app.component.html
) to create the user. Make sure you’ve subscribed to the createUserEvent$
using the async
pipe.
<button (click)="createUser()">create user</button>{{(createUserEvent$ | async) | json}}
Nice, open another browser and see it work in real-time.
That’s all you need to do to get it working.
I have both example projects on GitHub and each has more than just these subscription snippet examples.
shammelburg/express-graphql-api — GraphQL server
shammelburg/graphql-rxjs-angular — Angular application
My Experience
I’ve had a lot of fun putting these projects together and testing the functionality for both. I really like how subscriptions work and I can think of many scenarios where it would be useful.
I’m going to use GraphQL in my next project with Angular off course 😁 and it’s going to be a blast.
The great thing about using express is that you can still use REST endpoint for file uploads, etc.
There is still much to learn.
I hope you enjoyed this, thank you for reading.
Happy coding!