Getting started with GraphQL using Java applications

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:

graphql tutorial

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.

  1. Download the Galleon tool from here: https://github.com/wildfly/galleon/releases
  2. 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:

graphql java

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}

Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/micro-services/mp-graphql

References:https://www.wildfly.org/news/2020/08/13/Introducing-the-WildFly-GraphQL-feature-pack/