Times, when it was enough for web applications to be updated when user presses refresh in their browser window, are long ago gone. Usually, users want to get information updates as soon as possible. If you develop a web application with social functions like mailbox/chat or just want to add notification popup when an event occurs in the application, chances are that you consider to use some techniques/technologies that allow that. Typical approach would be to use either long-polling or websockets. Following article gives a brief insight on how to use latter approach together with GraphQL.

As many of you probably heard about GraphQL (https://graphql.org/learn/), which for quite a while was a hot topic among web developers. Developed by facebook and publicly released in 2015, it is query language for API. It is database and programming language agnostic, which means that you can freely choose whatever technology suits your specific task, since in its raw form GraphQL is a mere specification, although it does have canonical implementation written in Javascript  (https://github.com/graphql/graphql-js)

GraphQL is sometimes called REST killer. Although this statement is up to debate, it indeed solves some problems, which typically occur in REST-based API:

  1. It allows to decrease usage of network resources, since it allows API clients to fetch only that data, that is needed. Also operations that would typically require multiple requests in REST-based application can be done within single call to GraphQL API.
  2. It allows to decrease amount of endpoints needed for API clients to operate, since same GraphQL query may be used with different configuration, that is suitable for that specific client/operation.
  3.  It is self-documentary. No need for additional tools like swagger, since GraphQL requires server to define schema, which describes every operation and data types, provided by API.

If your project has API, chances are that you are either already using GraphQL or consider to migrate to it in near future. If not, then, maybe, this article will give you another reason to consider doing this.

GraphQL specification describes 2 operations that allow it to fully substitute REST. These are queries and mutations. Former ones allow clients to fetch data and latter allow to modify data and fetch the value after this modification. In terms of CRUD queries would correspond to read operations, while mutations would be responsible for creating, updating and deleting  (More in-depth information about queries and mutations can be found at https://graphql.org/learn/queries/). But, in addition to these, it also provides additional type of operations called subscriptions. Subscriptions are similar to queries with the exception that they do not instantly return data, but instead register a listener which will react to certain event. Since everything is better with examples, let’s create simple web-application that will periodically display random famous quote. For client we will use ReactJS and for server – spring boot and graphql-java (https://www.graphql-java-kickstart.com/tools/), but, as it was mentioned above, server can be rewritten in any other language that has GraphQL implementation available for it.  For those, who want to see it working, you can skip ahead and just take a look at https://github.com/fkryvyts-codete/graphql-subscription repository. It contains code for both server and client applications. For people, who do not want to skip, let’s do it step by step.

Server implementation

Pretty much everything in GraphQL starts with creating its schema. Schema also serves as some sort of contract between client and server applications, since once defined, both frontend and backend developers can work independently (frontend can mock data)  

graphql-subscription/server/src/main/resources/graphql/schema.graphqls

Type QuoteDto is our custom type, that describes data structure for quotes. Here we defined that every single quote will have 2 text fields: quote text and name of the person (author), who said this particular quote. Since we expect that these fields will always have some meaningful values different from null, we made them non nullable by appending their types with exclamation mark. Note, that by default each type in GraphQL is nullable.

Type Query is a reserved GraphQL type for queries. Every available query should have its definition present inside this section. Since GraphQL requires at least one query to be defined, we had to specify dummy query that returns nothing useful outside of “hello word” message.

Type Subscription is a reserved GraphQL type for subscriptions. Inside this section we can put definition for our custom subscription randomQuote. This definition describes which input parameters (none in this case) subscription expects and what is the return type (QuoteDto in our case) of it. Note, that since subscriptions do not return values directly, return type is the type of message, that listener, registered for this subscription, will receive.

Now, when we are done with schema, we need to actually provide an implementation for QuoteDto object inside our server’s code:

graphql-subscription/server/src/main/java/demo/dto/QuoteDto.java

This is simple data transfer object, which is not coupled with any database/storage engine or libraries like Hibernate, and can be populated however developer wants it.  

After this we also need to create resolvers for our custom query hello and subscription randomQuote, defined in schema before. Resolvers for queries and subscriptions in terms of standard REST-based API can be thought as controllers, that handle incoming requests (GraphQL also allows to define resolvers for individual fields in response, that work more like data mappers, but this is outside of the scope of this article).

Implementation for query resolver can be found inside repository and, since it does not do anything particularly useful, we can safely omit it and focus on creating a resolver for our subscription:

graphql-subscription/server/src/main/java/demo/graphql/resolver/SubscriptionResolver.java

Code is rather simple. Almost all actual logic for message publishing is located inside QuotePublisher service, which is injected via dependency injection. QuotePublisher is also fairly simple, since all it does, is that it broadcasts randomly picked quote from list of quotes every 5 seconds:

graphql-subscription/server/src/main/java/demo/graphql/publisher/QuotePublisher.java

Note, that Java implementation of GraphQL uses rxJava (https://github.com/ReactiveX/RxJava) to handle subscriptions. Without going into depth, what it does is that it provides implementation for Observer pattern (https://www.journaldev.com/1739/observer-design-pattern-in-java).  

After publisher is done, that’s all for the server, and we can start working on the client application.

Client implementation

For client, like everything else GraphQL-related, we could use plain Javascript without depending too much on 3rd party libraries. GraphQL handles subscriptions via websockets API, that allows to keep persistent bidirectional (unlike long-polling) connection between client and server:

But we also can use existing GraphQL clients that will handle things like reconnects and caching for us, as well as bindings for ReactJS.  For this example we will use Apollo Client (https://www.apollographql.com/docs/react/why-apollo.html)

Setup is quite straightforward, if you pay close attention to its official documentation.

We start by defining query for our subscription. Apollo client requires all queries to be in GraphQL AST (abstract syntax tree) form, so for this purpose we are using graphql-tag (https://www.npmjs.com/package/graphql-tag) library, that parses query strings and converts them to ast :

graphql-subscription/client/src/index.js

After that we can map it to ReactJS component by using Subscription component from react-apollo package:

graphql-subscription/client/src/index.js

Now, our custom Quote component can do all necessary work to map quote data and present it to end user.

Disregarding all boilerplate code for apollo client (like splitting requests between websockets and http transports based on operation type), which can be found in source code of full example, this is everything that is needed for client implementation.

Parameterized subscriptions

Since we rarely need to broadcast the exact same messages to all clients, we can take usage of GraphQL arguments and variables to specify, which exact messages should be send to particular client instance.  Let’s modify randomQuote subscription from our example, by introducing minimumLength parameter, which will define how long should be quote in order for it to be broadcasted to subscription listener. Since we want this parameter to be optional, we do not need to append it’s type with exclamation mark, which will allow parameter to be nullable:

graphql-subscription/server/src/main/resources/graphql/schema.graphqls

All GraphQL parameters are available in graphql-java resolvers as simple method arguments:

graphql-subscription/server/src/main/java/demo/graphql/resolver/SubscriptionResolver.java

We can easily filter quotes by their length with following code:

graphql-subscription/server/src/main/java/demo/graphql/publisher/QuotePublisher.java

As for client, we can just map ReactJS properties to GraphQL variables in following way:

graphql-subscription/client/src/index.js

Testing

As it was already mentioned, full code examples can be found in repository https://github.com/fkryvyts-codete/graphql-subscription . If you run both client and server (instructions about how to do this can be found inside README.md file in repository) then you will be able to see something similar to following:

You can open multiple instances of client application in different tabs/windows to see how message is broadcasted to every client. If different instances have different value for Minimum length, then it is possible that some quotes will be broadcasted only to one of the clients or none.

Conclusion

Hope that this article gave you some insights about abilities of GraphQL, which go beyond simple API with request->response model. Still, outside of the scope of this article were left such complex topics as authentication, since it is almost impossible both to cover all possible scenarios in single article and keep things simple. One thing that should be noted, that Apollo Client does support authentication via websockets (https://www.apollographql.com/docs/react/advanced/subscriptions.html#authentication)  and I advice to read documentation for any specific case that you might have, since it is quite rich.  

fedir.kryvytskyi

Fedir is a software engineer and Java enthusiast with a solid background in PHP. He enjoys finding simple and elegant solutions to complex problems. In his free time he writes novels and does 3D modelling.