Spring boot and GraphQL

A previous post Writing a GraphQL service using graphql-dotnet and ASP.NET core covered implementing an ASP.NET application with a GraphQL service.

As GraphQL is language/platform agnostic I wanted to see how to implement a similar little service using Java frameworks.

I’m using JetBrains’ IntelliJ (I think you need the ultimate edition for this) to create a project, so I’ll list a few steps regarding this process. In IntelliJ

  • Create a new Project
  • Select Spring Initializr
  • Leave the defaults as https://start.spring.io and just press the Next button
  • Supply a name for your project then press the Next button
  • At the next page you can select dependencies, just press the Next button
  • Finally give your project a name then press the Finish button

Now let’s update the pom.xml with the following

<!-- Add the following for GraphQL -->
<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>

<!-- Add this for the GraphQL interactive tool -->
<dependency>
   <groupId>com.graphql-java</groupId>
   <artifactId>graphiql-spring-boot-starter</artifactId>
   <version>4.0.0</version>
</dependency>

As shown with the comments, the last dependency will allow us to include the GraphQL interactive web application graphiql, obviously remove this if you do not need it.

Now let’s write some code…

Schema and Queries

Unlike the previous C# example, we’re going to use the GraphQL schema definition language to declare our schema (in C# we used code to implement this).

In your package folder add Person.java with the following code

public class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

It’s basic but this is our starting point (and matches what we did in the C# example).

We now need to create a QueryResolver, so add a file named Query.java to the package with the following code

import com.coxautodev.graphql.tools.GraphQLQueryResolver;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class Query implements GraphQLQueryResolver {
    List<Person> people = new ArrayList<>();

    public Query() {
        people.add(new Person("Scooby"));
        people.add(new Person("Shaggy"));
        people.add(new Person("Daphne"));
        people.add(new Person("Thelma"));
        people.add(new Person("Fred"));
    }

    public List<Person> people() {
        return people;
    }
}

That’s it for the Java code for now – Spring Boot’s dark magic will wire everything up for us, but before then we need to add a .graphqls file. Within the resources folder lets add a schema file named schema.graphqls (IntelliJ comes with a plugin for recognising this file type).

Note: the name of the file need not be schema, any name will suffice for this example.

Within the schema.graphqls file we have the following

type Query {
    people : [Person]
}

type Person {
    name: String!
}

That’s it!

Running the application will result in a Tomcat embedded server running on http://localhost:8080/graphiql (assuming you include graphiql in the Maven file). If we now execute the following query within graphiql

{
  people {
    name
  } 
}

we should see the result

{
  "data": {
    "people": [
      {
        "name": "Scooby"
      },
      {
        "name": "Shaggy"
      },
      {
        "name": "Daphne"
      },
      {
        "name": "Thelma"
      },
      {
        "name": "Fred"
      }
    ]
  }
}

Let’s now add an operation find to allow us to locate a Person by their name. In the Query class, add the following

public Person find(String input) {
   return people.stream()
      .filter(p -> p.getName().equalsIgnoreCase(input))
      .findFirst()
      .orElse(null);
}

This code acts a little like a Linq query in C#, returning an Optional, we’ll basically return a null if no item was found hence unwrapping our return from the Optional.

We need to add this operation to the schema.graphql Query type, so it now looks like this

type Query {
    people: [Person]
    find(input: String!) : Person
}

Running the application we can now write the following in graphiql

{
   find(input : "Scooby") {
      name
  }
}

Mutations

Next up, let’s write a mutation to add a person. We’re not going to add a Person to the list of people as this would require some refactoring of the query data, but it simply demonstrates the changes required to our project to implement mutations.

Let’s jump into the schema file and add mutation to the schema definition, so it looks like this

schema {
    query: Query
    mutation: Mutation
}

and now add the mutation type into the file, which should look like this

type Mutation {
    addPerson(input: String) : Person
}

Now we’ll create the code. Add a file (mine’s called Mutation.java) for our mutation code, here’s a the very simple example

import com.coxautodev.graphql.tools.GraphQLMutationResolver;
import org.springframework.stereotype.Component;

@Component
public class Mutation implements GraphQLMutationResolver {
    public Person addPerson(String input) {
        return new Person(input);
    }
}

That’s all there is to it, run the application and using grapiql, write the following in your query window

mutation addPerson($input : String) {
   addPerson(input : $input)
   {
      name
   }
}

and in the Query variables window add the following

{
  "input" : "Scooby Doo"
}

Subscriptions

Finally let’s write some subscription code. Change the schema.graphqls schema to look like this

schema {
    query: Query
    mutation: Mutation
    subscription : Subscription
}

and add the following type

type Subscription {
    personAdded : Person
}

The Subscription.java file will look like this

import com.coxautodev.graphql.tools.GraphQLSubscriptionResolver;
import org.reactivestreams.Publisher;
import org.springframework.stereotype.Component;

@Component
public class Subscription implements GraphQLSubscriptionResolver {

    private PersonPublisher publisher;

    public Subscription(PersonPublisher publisher) {
        this.publisher = publisher;
    }

    public Publisher<Person> personAdded() {
        return publisher.getPublisher();
    }
}

This implements our Subscription resolver, but we’ll separate out the publisher code to look like this. Here’s my PersonPublisher.java file

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.observables.ConnectableObservable;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;

@Component
public class PersonPublisher {

    private final Flowable<Person> publisher;

    public PersonPublisher() {
        Observable<Person> o = Observable.interval(1, 1, TimeUnit.SECONDS)
                .map(l -> new Person(l.toString()));
        ConnectableObservable<Person> co = o.share().publish();

        publisher = co.toFlowable(BackpressureStrategy.BUFFER);
    }

    public Flowable<Person> getPublisher() {
        return publisher;
    }
}

Note: I’ve based the code for the Subcription and PersonPublisher on the Subscription.java and the StockTickerPublisher.java sample code.

Code

Code is available on GitHub.

Disclaimer

graphiql does not display subscription events at this time. So I’ve not tested this subscription code. Hence beware, it may or may not work. I’ll try to update when I get chance to test it fully.