Continuum Logo Zwart

The term GraphQL is getting a lot of attention lately. As its name suggests, it’s a query language, just like SQL (Structured Query Language). Unlike SQL however, it’s used not in database interactions, but in web APIs. To explain the benefits it can bring there, we’ll explore GraphQL in a series of 3 blog posts, of which this one is the third:

  1. Context: in the blog post we’ll put GraphQL in its historical perspective and wider context, and will discuss its general pros and cons
  2. Syntax: this blog post will offer an introduction and general overview of the GraphQL syntax and how it’s used to collect data by the consumers of your API
  3. With Java and Spring: we’ll discuss 2 prominent Java libraries that enable you to create a GraphQL server API. The first relies on plain Java while the second build on the Spring framework
Graphql

Java/Spring libraries

Due to Spring Boot’s popularity and ease of use, we’ll explore the two most prominent Java GraphQL libraries as they can be used in a Spring Boot app. The libraries in question can of course be used outside a Spring Boot app, with additional configuration. The libraries in question are GraphQL Java and GraphQL SPQR, and we’ll also briefly mention the GraphiQL tool.

The GraphQL Java library

GraphQL Java is a low-level library in the sense that it offers all the tools for making a GraphQL API but requires a bit more orchestration. We’ll start off with the Spring Boot starter for GraphQL Java and the GraphQL Java Tools library, which simplifies the connection between your GraphQL schema and the classes you use to represent your custom types (those are the most recent versions at the time of writing).

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-spring-boot-starter</artifactId>
    <version>5.0.2</version>
</dependency>
<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphql-java-tools</artifactId>
    <version>5.2.4</version>
</dependency>

Besides that, we need to add at least one GraphQL schema file (with the .graphqls file extension). This will be picked up automatically by the Spring Boot starter, as long as the file or files are part of the classpath.

Next, we need to define one or more Spring beans that implement the GraphQLQueryResolver interface. The combination of those should cover the fields defined in the schema root (here students and student). The method names should either be the name of the field (students), the name prefixed with ‘get’ (getStudents) or the name prefixed with is (in case the return value is Boolean).

public class Query implements GraphQLQueryResolver {
    @Autowired
    private PersonRepository personRepository;

    public List<Person> getStudents() {
        return personRepository.findAllStudents();
    }

    public Person getStudent(long id) {
        return personRepository.findOneStudent(id);
    }
}

You’ll notice that the getStudent has a parameter that corresponds to the argument in de schema definition. As long as we use primitive types, the values will be mapped and converted automatically.

Our definition of Person includes a custom (non-scalar) field called address. Because GraphQL by design only does the work that’s needed (such as retrieving an address from the database), we need to set up field resolvers for non-scalar fields. To allow for this, the Person class needs to be a Java bean (i.e. simple POJO with getters and setters) with additional fields that hold information we need to do the lookups.

public class Person{
   private Long id;
   private String firstName;
   private String lastName;
   private String dateOfBirth;
   private Long addressId;

   ...
}

In the following resolver class, we can give GraphQL Java a method via which the address field can be filled in. In the case that null is returned, this will also work correctly, since the address field has not been defined as required (note that using Optional here would mean and do the same thing).

public class PersonResolver implements GraphQLResolver<Person> {
    @Autowired
    private AddressRepository addressRepository;
 
    public Address getAddress(Person person) {
        return addressRepository.findOne(person.getAddressId());
    }
}

Mutations work in exactly the same way, but with GraphQLMutationResolver as the interface that needs to be implemented.

public class Mutation implements GraphQLMutationResolver {
    private PersonRepository personRepository;
 
    public Person createStudent(String firstName, String lastName, String dateOfBirth) {
        Student student = new Student(firstName, lastName, dateOfBirth);
        return personRepository.save(student);
    }
}
The GraphQL SPQR library

GraphQL SPQR is a higher-level more opinionated library that builds upon the GraphQL Java library. It relies on annotations, similar to what typical JPA, and can be used in combination with Spring Data with a minimal amount of boilerplate code.

Here too, we can start off with a Spring Boot starter, though note that it is still in the early stages of development (and therefore not yet production-ready):

<dependency>
  <groupId>io.leangen.graphql</groupId>
  <artifactId>graphql-spqr-spring-boot-starter</artifactId>
  <version>0.0.4</version>
</dependency>

With GraphQL SPQR, you can rely on Java bean conventions and/or annotation scanning. For the next two examples, we assume that the class is declared as a Spring bean (e.g. via @Bean or @Component). The first method uses Java bean conventions, while the second uses annotation scanning.

@GraphQLApi
@WithResolverBuilder(BeanResolverBuilder.class)
@WithResolverBuilder(AnnotatedResolverBuilder.class)
private class MyOperationSource  {
    public String getGreeting() {
        return "Hello world !"; 
    }

    @GraphQLQuery
    public String personalGreeting(String name){
         return "Hello " + name + " !"; 
    }
}

With GraphQL SPQR, you can lean on Spring Data. In order to do that, we’ll add some JPA annotations as well as some GraphQL SPQR-specific annotations to our model.

@Entity
public class Person {
   @Id @GeneratedValue
   @GraphQLQuery(name = “id”, description = “A person’s id”)
   private String id;

   @GraphQLQuery(name = “firstName”, description = “A person’s first name”)
   private String firstName;

   @GraphQLQuery(name = “lastName”, description = “A person’s last name”)
   private String lastName;

   @GraphQLQuery(name = “dateOfBirth”, description = “A person’s date of birth”)
   private String dateOfBirth;

   @GraphQLQuery(name = “address”, description = “A person’s address”)
   private Address addressId;

   ...
}

If we then create a PersonService bean (be it via @Bean or @Component), we can add the following annotated method to implement the students query from our previous examples.

@GraphQLQuery(name = "students")
public List<Person> getStudents() {
    return personRepository.findAllStudents();
}

In the same way, we can implement the student query, which includes an annotated method parameter corresponding to the one defined in the GraphQL schema.

@GraphQLQuery(name = "student")
public Person getStudents(@GraphQLArgument(name = "id") long id) {
    return personRepository.findOne(id);
}

The createStudent mutation also follows the same pattern, though note that here we use the @GraphQLMutation annotation instead of the @GraphQLQuery annotation.

@GraphQLMutation(name = "createStudent")
public List<Person> getStudents(
    @GraphQLArgument(name = "firstName") String firstName,
    @GraphQLArgument(name = "lastName") String lastName,
    @GraphQLArgument(name = "dateOfBirth") String dateOfBirth,
) {
        Student student = new Student(firstName, lastName, dateOfBirth);
        return personRepository.save(student);
}

Finally, to bootstrap all the above into a functioning GraphQL API, we need to define a typical Spring MVC RestController class, where we set up a GraphQL object instance, that:

  • uses an annotation resolver builder, so that the annotated methods are picked up
  • looks up the queries and mutations found on the PersonService bean
  • uses Jackson for JSON mapping

And we define a MVC endpoint that takes requests and passes GraphQL responses.

@RestController
public class GraphQLSampleController {

    private GraphQL graphQL;

    @Autowired
    public GraphQLSampleController(PersonService personService) {
        GraphQLSchema schema = new GraphQLSchemaGenerator()
                .withResolverBuilders(new AnnotatedResolverBuilder())
                .withOperationsFromSingletons(personService)
                .withValueMapperFactory(new JacksonValueMapperFactory())
                .generate();
        graphQL = GraphQL.newGraphQL(schema).build();
    }

    @PostMapping(value = "/graphql",
        consumes = MediaType.APPLICATION_JSON_UTF8_VALUE,
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public Map<String, Object> graphql(@RequestBody Map<String, String> request, HttpServletRequest raw) {
        ExecutionResult executionResult = graphQL.execute(ExecutionInput.newExecutionInput()
                .query(request.get("query"))
                .operationName(request.get("operationName"))
                .context(raw)
                .build());
        return executionResult.toSpecification();
    }
}
GraphiQL

Although it’s not a library for creating GraphQL APIs, the fact that GraphiQL makes it incredibly easy to make GraphQL API calls, means that it’s invaluable in GraphQL API development.

If you’re using Spring Boot, you can use the following starter. If you’ve also included the GraphQL Java starter, the following which will start up the web GUI at /graphiql (which can be overridden in with the configuration property graphql.servlet.mapping).

<dependency>
    <groupId>com.graphql-java</groupId>
    <artifactId>graphiql-spring-boot-starter</artifactId>
    <version>5.0.2</version>
</dependency>

Notably, because GraphiQL relies on Kotlin 1.3 while Spring Boot currently sets it to Kotlin 1.2, you’ll need to add the following to the maven properties.

<properties>
  <kotlin.version>1.3.10</kotlin.version>
</properties>

By default, GraphiQL expects to find a GraphQL API under /graphql. If need be, that can be changed via the configuration property graphiql.endpoint.graphql). After that, you should be good to go.

Conclusion

I hope you’ve enjoyed this tour of GraphQL and that it has helped you to gain insights into when GraphQL can be useful, as well the basics of how to use it. For further information, I invite you to explore the official documentation of the GraphQL specification. It’s a real treasure trove and the perfect place to start your own GraphQL journey!

Alain Van Hout

Alain Van Hout

Java Software Crafter

 

Alain Van Hout is a Java Software Crafter with a Master in Science (Biology) and experience in academic research concerning evolutionary biology. Currently, Alain is working on web applications and API integrations at the genomics data analysis provider BlueBee (Mechelen), where he combines his background in biology with his passion for software Craftsmanship.