How to build GraphQL applications with Quarkus

GraphQL is an open-source query and data manipulation language for APIs. This article shows how to create and deploy a sample application using a Quarkus Runtime

What is GraphQL?

GraphQL is Query language for reading and mutating data in APIs. As a back-end developer, GraphQL provides a type system where you can describe a schema for your data. This, in turn, gives front-end consumers of the API the power to explore and request the exact data they need. Traditionally, web developers use REST to request and add data by hitting endpoints and fixed data sets.

Even though this approach has some clear advantages, there are also potential drawbacks:

  • REST Services can cause overfetching of data, by getting more information than you need. Besides, with every change that is made to the REST Client UI, there is a risk that there is more (or less) data required than before.
  • REST Services do not offer type-safety. GraphQL uses a strongly typed system to define the capabilities of an API using the GraphQL Schema Definition Language (SDL) and/or code-first.

If your product architecture requires attention on the above points, then GraphQL might be the perfect choice..

In this article, we discuss about running GraphQL on top of WildFly application server: Getting started with GraphQL using Java applications . In this article, we will learn how to run graphQL API in a Quarkus runtime and how to code a GraphQL Client to query for the data.

How to bootstrap GraphQL with Quarkus

In order to bootstrap our Quarkus application, we will need the following dependencies:

quarkus graphql

  • The first one, quarkus-smallrye-graphql, is what you need to build server side applications using GraphQL API
  • The second one, quarkus-smallrye-graphql-client, lets you use the @GraphQLClientApi to access your services from Java clients

Finally, looking at the pom.xml, this is the list of dependencies which will be added:

<dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-smallrye-graphql</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-graphql-client</artifactId>
</dependency>

Coding the GraphQL application

Firstly, let’s import the project in your IDE. Then, we can start adding classes. Our domain model consists of the following Classes:

graphql example quarkus

Here is the Country class:

public class Country {
    String name;
    String symbol;

  // Getters/Setters omitted for brevity
}

This is the Person class:

public class Person {

    String name;
    Country country;
    
   // Getters/Setters omitted for brevity	 
}

Then, in order to perform the CRUD operations on our Model classes, we will add the PersonService Class:

@ApplicationScoped
public class PersonService {

    List<Person> persons = new ArrayList();
    List<Country> countries = new ArrayList();
 

    public PersonService() {
        Country c1 = new Country("United States","US");
        Country c2 = new Country("Italy","IT");
        
        Person p1 = new Person("Benjamin Franklin",c1);
        Person p2 = new Person("Leonardo da Vinci",c2);
        
        persons.add(p1);
        persons.add(p2);
        
        countries.add(c1);
        countries.add(c2);
        
    }

    public List<Country> getAllCountries() {
        return countries;
    }

    public Country getCountry(int id) {
        return countries.get(id);
    }

    public List<Person> getAllPersons() {
        return persons;
    }

    public Person getPerson(int id) {
        return persons.get(id);
    }

    public List<Person> getPersonByCity(Country country) {
        return persons.stream()
                .filter(person -> person.getCountry().equals(country))
                .collect(Collectors.toList());
    }

    public void addPerson(Person person) {
        persons.add(person);
        countries.add(person.getCountry());
    }

    public Person deletePerson(int id) {
        return persons.remove(id);
    }

    public List<Person> getPersonByName(String name) {
        return persons.stream()
                .filter(person -> person.getName().equals(name))
                .collect(Collectors.toList());
    }

}

So far nothing exciting. Next, we will add the GraphQLApi class which is able to execute GraphQL Queries and Mutations:

@GraphQLApi
public class GraphQLService {

    @Inject
    PersonService personService;

    @Query("allCountries")
    @Description("Get all countries.")
    public List<Country> getAllCountries() {
        return personService.getAllCountries();
    }

    @Query
    @Description("Get a Country.")
    public Country getCountry(@Name("countryId") int id) {
        return personService.getCountry(id);
    }

    @Query("allPersons")
    @Description("Get all persons.")
    public List<Person> getAllPersons() {
        return personService.getAllPersons();
    }


    @Query
    @Description("Get a Person")
    public Person getPerson(@Name("personId") int id) {
        return personService.getPerson(id);
    }

    public List<Person> persons(@Source Country country) {
        return personService.getPersonByCity(country);
    }
    @Mutation
    public Person createPerson(@Name("person") Person person) {
        personService.addPerson(person);
        return person;
    }

}

In GraphQL, there are two types of operations you can perform: Queries and Mutations.

You can use a Query to fetch data. on the other hand, you will use a Mutation to modify server-side data.

You can think of a Query as an equivalent to GET calls in REST. Much the same way, a mutation represents the state-changing methods in REST (such as POST, DELETE, PUT, etc).

Testing the application with the UI

At first, we will be testing the application using the built-in UI which ships in the smallrye-graphql extension. The UI is available in dev/test mode at the following address: http://localhost:8080/q/graphql-ui/

Within the UI, we will test the Query allCountries including the name attribute in the response. Enter the following query to GraphiQL and press the play button:

{
    allCountries {
        name
    }
}

Here is your Query in action:

graphql tutorial

Besides, you can also test the following Queries which are available in this project :

  {
    allPersons {
        name
    }
  }

  query getPerson {
    person(personId: 0) {
        name
    }
  }

  query getCountry {
	country(countryId: 0) {
	name
	symbol
	persons {
	    name
	 }
	}
  }

Finally, the UI you can also test a Mutation. For example, let’s add a new Person which, in turn, also includes a new Country object:

mutation {
    createPerson(person: {name: "Isaac Newton", country: {name: "England", symbol: "GB"}}) {
        name
        country {
          name
          symbol
        }
    }
}

Here is the result from the UI:

graphql quarkus tutorial

Testing the application with Java clients

The built-in UI is an awesome short-cut for rapid test of your GraphQL operations. On the other hand, there is a variety of clients to test GraphQL. Here we will show how to use the Smallrye Microprofile Client Api for GraphQL.

To get started with this API, we will be using a typesafe Java clients which wraps the server methods with an interface contract. Besides, you will add the @GraphQLClientApi annotation to your interface with a reference to the GraphQLEndpoint:

@GraphQLClientApi(endpoint = "http://localhost:8080/graphql")
public interface PersonClientApi {

	public List<Country> getAllCountries(); 
	public List<Person> getAllPersons(); 
	public Country getCountry(@Name("countryId") int id);
	public Person getPerson(@Name("personId") int id);


}

Having your GraphQLClientApi interface available, then you can just inject it in your code to test the GraphQL server methods. For example, we will add a REST Endpoint to map the above methods:

@Path("/client")

public class ClientResource {

    @Inject
    PersonClientApi typesafeClient;

    @GET
    @Path("/persons")
    @Blocking
    public List<Person> getAllPersons() {
        return typesafeClient.getAllPersons();
    }
    
    @GET
    @Path("/person/{id}")
    @Blocking
    public Person getPerson(int id) {
		return typesafeClient.getPerson(id);
    }
 
    @GET
    @Path("/country/{id}")
    @Blocking
    public Country getCountry(int id) {
		return typesafeClient.getCountry(id);
    }

    
    @GET
    @Path("/countries")
    @Blocking
    public List<Country> getAllCountries() {
        return typesafeClient.getAllCountries();
    }
    
}

For example, let’s fetch one Person from the list as follows:

curl -s http://localhost:8080/client/person/0 | jq
{
  "country": {
    "name": "United States",
    "symbol": "US"
  },
  "name": "Benjamin Franklin"
}

Finally, we will show how to use a Dynamic Client which lets you build dynamically the list of fields from a Query name. In this example, we will fetch the Person name from the allPerson Query:

@Inject
@GraphQLClient("query-dynamic")
DynamicGraphQLClient dynamicClient;

@GET
@Path("/dynamic")
@Blocking
public List<Person> getAllPersonsUsingDynamicClient() throws Exception {
  Document document = document(    
            operation(field("allPersons",    	                
                field("name"))));
        Response response = dynamicClient.executeSync(document);  
         
        JsonArray personArray = response.getData().getJsonArray("allPersons"); 
    
        List<Person> persons = response.getList(Person.class, "allPersons"); 
        return persons;
        

}

Please notice a dynamic query requires some configuration, at least the URL of the remote service. We can either set that within the @GraphQLClientApi annotation (through endpoint parameter), or within the application.properties. We will use the latter approach adding the quarkus.smallrye-graphql-client property for our Client:

quarkus.smallrye-graphql-client.query-dynamic.url=http://localhost:8080/graphql

Testing the above Query:

curl -s http://localhost:8080/client/dynamic | jq
[
  {
    "name": "Benjamin Franklin"
  },
  {
    "name": "Leonardo da Vinci"
  }
]

Conclusion

This article was a walk through the design and configuration of a Microprofile GraphQL application using Quarkus as Runtime. You can find the source code for this project here: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/graphql-demo