This tutorial will introduce you to the GraphQL Microprofile specification covering the basics of this emerging technology and showing a full example application developed on WildFly application server, which is a Microprofile compatible runtime environment.
GraphQL is a specialized query language with a syntax that defines the process of data-request and is useful when conveying data to a client from a server. One of GraphQL’s key aspect is its ability to aggregate data from various sources with a single endpoint API.
GraphQL is already widely used in Microservices architectures and there are several Java-based GraphQL libraries available. Now a specification for GraphQL is available in the MicroProfile project and we can expect that will help in increasing the popilarity of this API and reach both user community and vendor support.
GraphQL in a nutshell
Let’s start with the basic GraphQL Types.
The Query type describes the data that you want to fetch from the GraphQL server.
GraphQL queries access not just the properties of one resource but also smoothly follow references between them. While typical REST APIs require loading from multiple URLs, GraphQL APIs get all the data your app needs in a single request.
For example, let’s consider the following relation:
The query below will fetch all the users and their publicly visible todos:
query { users { name todos { title } } }
The Mutation is the second kind of “root type” which lets us write data to the DB. Think of Mutation as analogous to POST and PUT requests of REST. Let’s look at an example:
mutation createUser{ addUser(name: "John", age: 34) { id } }
What this means is, we are defining a mutation called “createUser” that adds a user with fname, “John” and age, “34”. The response type of this mutation is the function body. It responds with a JSON with the id of the posted data.
data : { addUser : "a21c2h" }
The third type of operation available in GraphQL is Subscription. A Subscription gives the client the ability to listen to real-time updates on data. Subscriptions use web sockets under the hood. Let’s take a look at an example:
subscription listenLikes { listenLikes { name likes } }
The above subscription responds with the list of users with their first name and number of likes whenever the like count of the user changes.
Setting up a GraphQL Server with WildFly
In order to setup a GraphQL server that responds for queries, mutations and subscriptions we will create a custom WildFly distribution using Galleon provisioning tool.
- Download the Galleon tool from here: https://github.com/wildfly/galleon/releases
- Then, download the provision.xml file which contains the feature pack definition for GraphQL: https://github.com/wildfly-extras/wildfly-graphql-feature-pack/blob/master/provision.xml
Now, create the custom WildFly distribution using the galleon.sh script, found in the bin folder of the Galleon tool:
$ ./galleon.sh provision ./provision.xml --dir=wildfly-graphql
A folder named “wildfly-graphql” will be created with the latest WildFly version. The XML configuration file includes the graphql extension in it:
<extension module="org.wildfly.extension.microprofile.graphql-smallrye"/>
Creating a GraphQL application
Our example consists of a CDI Bean annotated with @GraphQLApi. This annotation indicates that the CDI bean will be a GraphQL endpoint.
The Bean contains a couple of @Query methods and two @Mutation methods. The @Query methods are used to retrieve a graph from the Item object. The @Mutation are used to insert or remove an existing Item:
package com.mastertheboss.graphql.sample; import com.mastertheboss.graphql.model.Item; import org.eclipse.microprofile.graphql.*; import javax.enterprise.context.ApplicationScoped; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @GraphQLApi @ApplicationScoped public class ItemService { List items = new ArrayList<>(); @Query("item") @Description("Get a single Item by id") public Item getItem(@Name("id") Integer id){ return findItem(id); } @Query("allItems") @Description("Get all Items") public List getAllItems() { return items; } @Mutation public Item createItem(@Name("item") Item item) { items.add(item); return item; } @Mutation public Item deleteItem(Integer id) { Item item = findItem(id); items.removeIf(e -> e.getId().equals(id)); return item; } public Item findItem(Integer id) { Item item = items.stream().filter(a -> a.getId() == id).collect(Collectors.toList()).get(0); return item; } }
The Item object is a simple Java Bean. For the sake of brevity, just fields are included here:
public class Item implements Serializable { private Integer id; private String type; private String model; private int price; // Getters and Setters omitted }
Now, we will build and deploy the application on WildFly and show different ways to test it.
Building the GraphQL example
In order to build the GraphQL example application, it is recommended to include the pom file for the microprofile-graphql-api. Then, include the smallrye-graphql-ui-graphiql:
<dependency> <groupId>org.eclipse.microprofile.graphql</groupId> <artifactId>microprofile-graphql-api</artifactId> <version>1.0.3</version> <type>pom</type> <scope>provided</scope> </dependency> <dependency> <groupId>io.smallrye</groupId> <artifactId>smallrye-graphql-ui-graphiql</artifactId> <scope>provided</scope> </dependency>
You can deploy the Web application as a regular WildFly application:
mvn clean install wildfly:deploy
Retrieving the GraphQL schema
Once the ItemService is deployed a schema will be automatically generated, from the @Query and @Mutation annotations we have included in our Service. In order to retrieve the existing schema, we can execute a simple request. Assumed that the application is running under the “mp-graphql” Web context, the schema is available under the “graphql” Path:
$ curl http://localhost:8080/mp-graphql/graphql/schema.graphql type Item { id: Int model: String price: Int! type: String } "Mutation root" type Mutation { createItem(item: ItemInput): Item deleteItem(id: Int): Item } "Query root" type Query { "Get all Items" allItems: [Item] "Get a single Item by id" item(id: Int): Item } input ItemInput { id: Int model: String price: Int! type: String }
Testing with the GraphQL User Interface
The simplest way to test our GraphQL Query and Mutation is via the UI which is available once we have included the following dependency in our application:
<dependency> <groupId>io.smallrye</groupId> <artifactId>smallrye-graphql-ui-graphiql</artifactId> <version>1.0.7</version> </dependency>
The GraphQL will be available upon deployment of your application under the Context http://localhost:8080/[app-context]/graphql-ui/
Let’s start by adding an Item object using the available Mutation:
mutation Add { createItem(item: { id: 1, type: "Laptop" model: "Lenovo" price: 500 } ) { type model price } }
Here is the expected result from the UI:
Next, let’s try to execute a Query to fetch all available Item objects, returning a Graph which includes just the “type” and “model” fields:
query all { allItems { type model } }
The above Query returns:
{ "data": { "allItems": [ { "type": "Laptop", "model": "Lenovo" } ] } }
You might have also executed the following Query which fetches a single Item by id:
query item { item(id: 1) { type model } }
Finally, let’s delete the existing Item with the following Mutation:
mutation Delete { deleteItem(id :1){ price model } }
That will return a Graph of the deleted Item:
{ "data": { "item": { "type": "Laptop", "model": "Lenovo" } } }
Testing with cURL
Since curl is a command line tool available on any linux machine, it’s the simplest way to operate with GraphQL Queries that are not particularly complex. Without further ado, here’s a curl command that will fetch a GraphQL query from our API, which returns the temperatureF attribute for one location:
curl -s -X POST -H "Content-Type: application/json" --data '{ "query": "{ allItems { type model} }" }' http://localhost:8080/mp-graphql/graphql | jq
The returned output is:
{ "data": { "allItems": [ { "type": "Laptop", "model": "Lenovo" } ] } }
Testing with a Java Client
A GraphQL service can also be tested with a bare simple Java Client, for example a Servlet, which executes the available GraphQL query:
package com.mastertheboss.graphql.sample.client; import com.mastertheboss.graphql.model.Item; import io.smallrye.graphql.client.typesafe.api.GraphQlClientBuilder; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @WebServlet("/demo") public class GraphQLServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ItemApi api = GraphQlClientBuilder.newBuilder().endpoint("http://localhost:8080/"+ request.getContextPath()+"/graphql").build(ItemApi.class); List list = api.getAllItems(); for (Item i:list) { response.getWriter().append(i.toString()); } } }
The GraphQlClientBuilder registers a new endpoint for WeatherApi using the application’s Web context. The ItemAPI is merely an interface containing the available Query methods available:
public interface ItemApi { public Item getItem(@Name("id") Integer id); public List getAllItems(); public Item createItem(Item item); public Item deleteItem(Integer id); }
In order to build an application which includes GraphQL client API, you have to include also the following dependency:
<dependency> <groupId>io.smallrye</groupId> <artifactId>smallrye-graphql-client</artifactId> <version>1.0.7</version> </dependency>
Now you can test your Servlet Client as follows:
$ curl http://localhost:8080/mp-graphql/demo
Item{id=1, type='Laptop', model='Lenovo', price=500}
Conclusion
This article discussed about using Microprofile GraphQL API on WildFly application server. if you want to learn how to run and test a GraphQL application on Quarkus we recommend checking this article: Getting started with GraphQL using Java applications
Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/micro-services/mp-graphql