GraphQL Subscriptions with Ballerina: A Step-By-Step Guide
GraphQL is comparatively a newer technology in the software industry though it has gained significant growth in recent years. It becomes so popular since it could address many issues in APIs that had been there for over the years. There are three main operations in GraphQL. And subscription is one of them. Using subscriptions, clients can keep getting updates from the server without using any polling method. Therefore subscriptions can make a positive impact on the performance in real-time applications. You can use the following links to get into more details about GraphQL and GraphQL Subscriptions.
- Introduction to GraphQL: https://graphql.org/learn/
- Subscriptions in GraphQL and Relay: https://graphql.org/blog/subscriptions-in-graphql-and-relay/
Previously, we have discussed implementing GraphQL Subscriptions using JavaScript. In this article, we are going to try GraphQL subscriptions using the Ballerina language. In case you are not familiar with Ballerina, it is an open-source programming language generally used in cloud-native technologies. You can refer to the following link and get a proper idea about Ballerina.
- Learn Ballerina: https://ballerina.io/learn/
Why Ballerina?
Ballerina has many excellent features which provide a better experience with GraphQL for the users. One thing is that Ballerina supports the code-first approach in GraphQL. It means the GraphQL engine builds the schema itself according to the user-defined functions. Therefore users don’t need to worry about the schema configurations. And also implementing resolver functions is not complicated at all. In this article, you will see how a GraphQL server can be easily implemented in Ballerina just in a few steps.
Let’s Start
In a previous article, we implemented a real-time chat application using GraphQL subscriptions and JavaScript. We are going to use the same design we used in that article and try it with Ballerina.
You can download Ballerina from here. If you are already installed Ballerina, try to update its version by using this command.
bal dist update
Now we can start the server-side implementation for the chat application. For that, we have to create a new package first.
bal new your_package_name
Once it is completed we will see the following files in the package directory.
Ballerina.toml : this file includes the package descriptions and local dependencies for your package.
main.bal : the file containing the source code.
Now open the main.bal file and import the following library.
import ballerina/graphql;
This is a standard library in Ballerina which provides APIs to interact with GraphQL.
Writing Resolvers
Now we are going to write the necessary resolver functions to run the chat application. For a demonstration, I will take a GraphQL schema used in my previous article which is used to implement a chat application, and write the resolver functions in Ballerina according to that schema.
This is the schema we used in the previous article.
type Query {
viewMessages: [Message!]
}
type Mutation {
sendMessage(name: String, content: String): Message!
}
type Subscription {
receiveMessage: Message!
}type Message {
id: ID!
name: String!
content: String
}
According to the schema we have to write three resolvers and one type record
for Message
. Starting with Query, the field name is viewMessages
and its type is [Message!]
. It means the field will return an array of JSON objects with the type Message
and the objects cannot be null.
Since it is a query, the resolver function must be a resource
function with the get
accessor. The field name in the schema must be the function name. And the return type must be similar to the type of the field in the schema.
resource function get viewMessages() returns Message[]? {
//
}
And the mutation resolver must be a remote
function.
remote function sendMessage(string name, string content) returns Message {
//
}
In Ballerina, subscription resolvers always return a stream
. And it must be a resource
function. And the subscribe
accessor must be used here. The subscription resolver is going to look like this.
resource function subscribe receiveMessage() returns stream<Message, error?> {
//
}
And there must be a record
in Ballerina to implement Message
type given in the schema.
type Message record {|
string id;
string name;
string? content;
|};
The current view of the main.bal
would be like this.
Now let’s implement the functions to keep track of messages in the service. For that, we will use an array of the Message
type. And it has to be instantiated in the constructor. In the viewMessage()
function, the requirement is to show all the stored messages. For that, we have to return the Message
array here.
In the sendMessage()
function, the requirement is to create a new message from the given data and store it. Here we have to create a new Message
record and push it to the Message
array.
Pub/Sub Model
Now let’s move on to implementing the subscription function. To do that, we need to import nuvindu/pubsub
library into the source code.
import nuvindu/pubsub;
This library is created to use Pub/Sub APIs in Ballerina. If you need to learn about the Pub/Sub model in detail, you can read my previous article about Kafka Pub/Sub here. Once you understand the role of Pub/Sub
in subscriptions, we can move on to the next steps.
Now we can implement a Pub/Sub
instance in the Ballerina service. First, we need to instantiate a Pub/Sub
object in the constructor. Then we have to use it inside the sendMessages()
method (the method that is used to put data into the data store) to publish new messages into a Pub/Sub Topic
. Then we need to retrieve the published data sequentially from the receiveMessage()
method (the subscription resolver). For that, we can use the subscribe
API in Pub/Sub
. Then we have to subscribe to the same topic
, that the data has been published. Since the receiveMessage()
method (the subscription resolver) requires a stream
to return, the subscribe
API can provide the exact type of stream that the user has requested. If it is a bit confusing, just look at the following code and see how it is used. (Line no: 25)
*** The check
expression is used in Ballerina ( Line no: 20, 25 ) to return the error value whenever an API sends an error
. Find out more about this from here.
Testing the Subscriptions
Now we have come to the testing stage. We can either use GraphiQL or GraphQL playground for that. If the GraphiQL web client is used, the following configuration must be in the Ballerina service.
@graphql:ServiceConfig {
graphiql: {
enabled: true
}
}
After adding that, the final version of the main.bal
file will look as below.
Use the bal run
command, to run the service. If the service is executed without an error, we can test our project now. If the GraphiQL web client is enabled as above, we can use http://localhost:4000/graphiql
and it will redirect to the GraphiQL IDE.
Now what we are going to do is constantly send messages to the server while running a subscription query. Then we can see how the subscription query returns responses according to the new values. For that, we have to run a subscription query in one window and run mutations in another.
First, run the following query in the GraphiQL IDE.
subscription receive {
receiveMessage{
id
name
content
}
}
Now in another window with the same address, run the following mutation.
mutation send {
sendMessage(name: "User", content: "Hello"){
id
name
content
}
}
If we go back to the previous window now, we can see a new response has arrived for the subscription request.
Now you can send different types of messages using mutations and see how each time the response of the subscription is updated with the data.
Here you can find the complete code for this tutorial. If you got any issues regarding this, feel free to leave a comment.