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.