Try implementing GraphQL server in Java

Hello. Personally, I had the opportunity to implement a GraphQL server in Java, so this time I would like to write about that finding. This article is the 18th day article of GraphQL Advent Calendar 2020.

Introduction

This article does not provide an overview of GraphQL or a description of the Spring Boot and its peripheral libraries used in its implementation. I think that those who know them to some extent will be the target, but since I have not written anything so difficult, I think that you can probably understand it in the atmosphere.

Find a library

First, find the libraries you need to implement your GraphQL server.

Two were found. Both seem to be usable in combination with Spring Boot. Looking at the implementation example of GraphQL Resolver, GraphQL Java Kickstart seemed to be simpler to write, so I will use this one this time.

Looking at the GraphQL Java Kickstart README,

This project wraps the Java implementation of GraphQL provided by GraphQL Java.

It wraps GraphQL Java. Is written. So that's it.

Decide which function to implement

Next, decide which features to implement using GraphQL. This time, I decided to implement the basic functions using Query, Mutation, and Subscription. The use cases of the function are roughly as follows.

Configure the project

Now let's create an application. Create a project with Spring Initializer as a template. Only Lombok is added here because we will add the necessary dependencies later. spring_initializer.png

Create an application

build.gradle First, modify build.gradle.

Add com.graphql-java-kickstart: graphql-spring-boot-starter to dependency. Also add com.graphql-java-kickstart: graphiql-spring-boot-starter to test the API on GraphQL. In addition, the dependencies of io.projectreactor: reactor-core, spring-actuator, and micrometer-registry-prometheus have been added. The former is for Subscription implementation and the latter is for Metrics acquisition.

build.gradle


plugins {
	id 'org.springframework.boot' version '2.4.1'
	id 'io.spring.dependency-management' version '1.0.10.RELEASE'
	id 'java'
}

sourceCompatibility = '11'

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:8.0.0'
	runtimeOnly 'com.graphql-java-kickstart:graphiql-spring-boot-starter:8.0.0'
	// To embed GraphiQL tool
	implementation 'com.graphql-java-kickstart:graphiql-spring-boot-starter:8.0.0'
	// For subscripion
	implementation 'io.projectreactor:reactor-core:3.4.1'
	// For metrics
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'io.micrometer:micrometer-registry-prometheus:1.6.0'

	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'
}

application.yml Next, create application.yml.

application.yml


# 1. To enable graphiql
graphiql:
  mapping: /graphiql
  endpoint:
    graphql: /graphql
# 2. To enable graphql metrics
graphql:
  servlet:
    actuator-metrics: true
# 3. To enable to get metrics of spring-actuator from /actuator/prometheus
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus

schema.graphqls Create schema.graphqls to define the GraphQL schema. Register schema.graphqls under src/main/resources/graphsql.

schema.graphqls


type Query {
    bookById(id: ID): Book
    books: [Book]!
}

type Book {
    id: ID
    name: String
    pageCount: Int
}

type Mutation {
    registerBook (
        id: ID
        name: String
        pageCount: Int
    ): Book
}

type Subscription {
    subscribeBooks: Book!
}

Java Model GraphQL Java requires Java classes defined in the schema. First, create a Java Model for Book Type.

Book.java


@AllArgsConstructor
@Data
public class Book {
    private String id;
    private String name;
    private int pageCount;
}

Resolver Next, create a Resolver that implements Query, Mutation, and Subscription. It is necessary to create the following implementation classes for each, and make the query name defined in the schema and the implemented method name the same. Also, the implementation class is registered as a Spring Bean.

DataProvider and IBookProcessor are your own classes. Perform the following processing.

BookResolver.java


@Slf4j
@AllArgsConstructor
@Component
public class BookResolver implements GraphQLQueryResolver,
                                     GraphQLMutationResolver,
                                     GraphQLSubscriptionResolver {
    private final DataProvider dataProvider;
    private final IBookProcessor bookProcessor;

    /**
     * Query: Get all books.
     */
    public List<Book> books() {
        return dataProvider.books();
    }

    /**
     * Query: Retrieve a book by id.
     */
    public Book bookById(String bookId) {
        return dataProvider.bookById(bookId);
    }

    /**
     * Mutation: Register a book.
     */
    public Book registerBook(String id, String name, int pageCount) {
        final Book book = new Book(id, name, pageCount);
        dataProvider.books().add(book);

        // Emit an event for subscription.
        bookProcessor.emit(book);
        return book;
    }

    /**
     * Subscription: Publish an event that a book is registered.
     * Need to return Publisher on reactive-streams.
     */
    public Publisher<Book> subscribeBooks() {
        return bookProcessor.publish();
    }

    /**
     * Error handler. can handle an throwable that occurs in resolver execution.
     */
    @ExceptionHandler(Throwable.class)
    GraphQLError handle(Throwable e) {
        log.error("Failed to execute resolver.", e);
        return new ThrowableGraphQLError(e, "Failed to execute resolver.");
    }
}

I tried to make it easy to understand. service_image.png

Run the application

Run the application you created. Start the SpringBoot application, access the GraphQL endpoint (http: // localhost: 8080/graphiql) with a browser, and execute Query.

  1. Execute the above Subscription.
  2. Open another browser and execute Mutation.
  3. The information of the registered book is displayed in the result of Subscription.

Metrics were also collected correctly. I use a tool called Metricat on the following site for graphing.

If you set the prometheus endpoint of spring-actuator (http: // localhost: 8080/actuator/prometheus) to the Prometheus exporter URL of Metricat and then execute the query with GraphiQL, the following graph will be displayed. I will.

metrics.png

Summary

So far, we have described how to implement GraphQL server in Java. The implemented code is just a sample code level, but it was easy to develop such as Resolver, and it was not bad for usability.

I have no plans to use it in practice yet, but I've listed some points that I'm curious about when I imagine using it in practice.

The source code used is registered on the following GitHub. I would appreciate it if you could refer to it.

Recommended Posts

Try implementing GraphQL server in Java
Try implementing Android Hilt in Java
Try implementing GraphQL server using grahpql-java-tools (+ kotlin)
Try implementing Yubaba in Kinx
Try using RocksDB in Java
Try calling JavaScript in Java
Try developing Spresense in Java (1)
Try functional type in Java! ①
It's late! Try implementing Android Notification in Java (Beginner)
Try implementing asynchronous processing in Azure
It's late! Try implementing Android Work Manager in Java (Beginner)
Try implementing signature verification for elliptic curve cryptography in Java
Try running Selenuim 3.141.59 in eclipse (java)
Try an If expression in Java
Try running AWS X-Ray in Java
Try to implement Yubaba in Java
Java: Try implementing your own comma-separated formatter
Try to solve Project Euler in Java
Try to implement n-ary addition in Java
Try using the Stream API in Java
Try using JSON format API in Java
Try calling the CORBA service in Java 11+
Try making a calculator app in Java
Get history from Zabbix server in Java
Partization in Java
Try Java 8 Stream
Changes in Java 11
Rock-paper-scissors in Java
Pi in Java
Roughly try Java 9
FizzBuzz in Java
[AWS IoT] Implementing Authorizing Direct Calls in Java [Java]
Try scraping about 30 lines in Java (CSV output)
Try to create a bulletin board in Java
Second decoction: Try an If expression in Java
Try using Sourcetrail (win version) in Java code
Try using GCP's Cloud Vision API in Java
Try using Sourcetrail (macOS version) in Java code
Try communication using gRPC on Android + Java server
Try using the COTOHA API parsing in Java
Interpreter implementation in Java
Make Blackjack in Java
Try calling synchronized methods from multiple threads in Java
Rock-paper-scissors app in Java
Constraint programming in Java
Put java8 in centos7
NVL-ish guy in Java
"Hello World" in Java
Callable Interface in Java
Try implementing the Eratosthenes sieve using the Java standard library
Comments in Java source
Azure functions in java
Try LetCode in Ruby-TwoSum
Format XML in Java
Simple htmlspecialchars in Java
Boyer-Moore implementation in Java
Hello World in Java
Use OpenCV in Java
webApi memorandum in java
Type determination in Java
Ping commands in Java