REST is History, Let’s Do GraphQL (with Ballerina)

Thisaru Guruge
Ballerina Swan Lake Tech Blog
5 min readJun 29, 2021

This article explains how to convert a REST API to a GraphQL API using Ballerina.
Ballerina is an open source language mainly focused on network and cloud native programming. If you haven’t installed Ballerina yet, you can download it from here. You can Learn Ballerina easily too!

I am not wrong if I state that the REST APIs have been dominating the computing world for a long time now. They are in mobile applications, web applications, embedded systems, etc. We see REST APIs everywhere!

Even though the REST APIs have a lot of great features, and they have been a great asset when it becomes to integrations, there are some drawbacks.

To show one of these drawbacks, let’s consider the weatherapi.com, a free weather API. It provides a REST API to retrieve weather information of a given location.

You need an API key to retrieve weather data from the weatherapi.com. An API key can be created for free from the weatherapi.com.

Say we have a mobile app, where we want to get the current temperature and the time of a provided city. To get these data we have to send a GET request to the following endpoint:

http://api.weatherapi.com/v1/current.json?key=<API_KEY>&q=<LOCATION>

This will return a JSON object, similar to the following:

{
"location": {
"name": "Colombo",
"region": "Western",
"country": "Sri Lanka",
"lat": 6.93,
"lon": 79.85,
"tz_id": "Asia/Colombo",
"localtime_epoch": 1624954019,
"localtime": "2021-06-29 13:36"
},
"current": {
"last_updated_epoch": 1624950000,
"last_updated": "2021-06-29 12:30",
"temp_c": 31.0,
"temp_f": 87.8,
"is_day": 1,
"condition": {
"text": "Partly cloudy",
"icon": "//cdn.weatherapi.com/weather/64x64/day/116.png",
"code": 1003
},
"wind_mph": 12.5,
"wind_kph": 20.2,
"wind_degree": 220,
"wind_dir": "SW",
"pressure_mb": 1008.0,
"pressure_in": 30.2,
"precip_mm": 0.9,
"precip_in": 0.04,
"humidity": 75,
"cloud": 75,
"feelslike_c": 37.5,
"feelslike_f": 99.6,
"vis_km": 10.0,
"vis_miles": 6.0,
"uv": 6.0,
"gust_mph": 13.4,
"gust_kph": 21.6
}
}

Then we have to process this (large?) JSON object, and get the current temperature and the time of the location. But do we need all these other information? No!

If we look closely, we have retrieved 33 fields (ignoring the intermediate levels), where we actually need just 2 fields! Consider the amount of network bandwidth and memory we are wasting (which is crucial when it comes to mobile apps).

This is the problem called over-fetching in REST APIs. We have to fetch data we do not need, in order to retrieve the data we need.

There’s another major issue called under-fetching, which is the opposite of over-fetching, where we have to call multiple APIs to get the data, because one API does not provide all the information you need. This article will not discuss that here, which will be a separate article.

GraphQL overcomes this over-fetching issue by defining the data in a structured manner, where users can request exactly what they want, and the service will serve exactly what they asked.

But, what about the existing REST APIs? Can we migrate all these to GraphQL? The answer is YES!

This is where the Ballerina GraphQL becomes handy. In this article I am going to show how to convert this weather REST API to a GraphQL API using Ballerina.

Let’s go back to the start. We can retrieve the data from the weather API using Ballerina HTTP module. Check the following code:

import ballerina/http;http:Client httpClient = check new("http://api.weatherapi.com/v1");
configurable string API_KEY = ?;
const CURRENT_WEATHER_ENDPOINT = "/current.json";
function getCurrentWeather(string city) {
string query = string`?key=${API_KEY}&q=${city}`;
return httpClient->get(CURRENT_WEATHER_ENDPOINT + query);
}

The API_KEY here is defined as a configurable variable, for which the actual value can be provided when the program is running. To learn more about the configurable variables, visit Ballerina Learn pages.

Here, the data json object (json is a built in type in Ballerina, if you’re wondering) contains all the information we need. Actually, it contains more than the information we actually need. (That’s the problem we are going to solve, remember?). The JSON object returned here as the data is similar to the one shown above.

Now we are going to write a GraphQL service using Ballerina, to serve these information. We can use the above HTTP client code for this.

As mentioned above, GraphQL defines structured data types to serve. We are going to define these types in Ballerina first.

type WeatherResponse record {|
Weather current;
Location location;
|};
type Weather record {
float temp_c;
};
type Location record {
string localtime;
};

Check how the record types are defined here, matching with the above JSON object. In the response, we have two main fields, current and location. We create a WeatherResponse record to match that. The location field is of type Location, and the current field is of type Weather. Since these two are the only fields in the top-level JSON object, it is defined as a closed record.

For the Weather and Location records, we do not need all the fields inside them (for now). Therefore, only the required fields are defined here. (Note the field names are exactly the same as the field names in the JSON object, which is important).

Now we can update the previous HTTP client code as follows:

import ballerina/http;http:Client httpClient = check new("http://api.weatherapi.com/v1");
configurable string API_KEY = ?;
const CURRENT_WEATHER_ENDPOINT = "/current.json";
function getCurrentWeather(string city) returns json|error {
string query = string`?key=${API_KEY}&q=${city}`;
return httpClient->get(CURRENT_WEATHER_ENDPOINT + query);
}

This new function getCurrentWeather is returning the JSON object of the current weather for a given location, or an error if an error occurred while calling the get method of the Ballerina HTTP client.

Now we can write a GraphQL service to return aWeatherResponse object.

import ballerina/graphql;service /weather on new graphql:Listener(4000) {
resource function get weather(string city) returns WeatherResponse|error {
json weatherResponseJson = check getCurrentWeather(city);
return weatherResponseJson.cloneWithType();
}
}

That’s it! Now we are serving these details as a GraphQL service! This service can be used as a gateway to access to the weather API through GraphQL.

The following is the complete code (in a single ballerina file) for the example this article explained.

You can try this out by running this using the following bal commnd

bal run weather_graphql.bal -- -CAPI_KEY=<YOUR_WEATHER_API_KEY>

After running, we can test this out using a GraphQL client such as GraphQL PlayGround or Altair GraphQL Client, or just by a simple cURL command like below:

curl -X POST -H "Content-type: application/json" -d '{ "query": "{ weather(city: \"colombo\") { current { temp_c } location { localtime } } }" }' 'http://localhost:4000/weather'

In this cURL command, we send the request with the following GraphQL document

{
weather(city: "colombo") {
current {
temp_c
}
location {
localtime
}
}
}

The response from the server is the following:

{
"data": {
"weather": {
"current": {
"temp_c": 30
},
"location": {
"localtime": "2021-06-29 15:10"
}
}
}
}

Now there’s only the information we actually requested, nothing more, nothing less.

This GitHub repository contains a complete ballerina package example for converting a REST API to a GraphQL API.

As the Ballerina language, the Ballerina GraphQL module too is an open source project.

Feel free to contribute by reporting issues, requesting new features, or even sending pull requests. GitHub stars are always welcome :)
https://github.com/ballerina-platform/module-ballerina-graphql

--

--