Creating a client app

Introduction

Since the 1.12 release, the recommended way to build a client with this plugin, is to use the Spring client. It adds some latency when starting the app, but it allows all the "magic" of Spring.

But you're not obliged to do so, and this page describes how to create a Non Spring client app, based on the code generated by this plugin.

Tutorials

This page is an overview. It contains all the important information on how to create the app.

You'll find a detailed tutorial, with all steps on how to use the client, with two versions: a Gradle client tutorial and a a Maven client tutorial.

Summary

The client mode makes it easy for a Java GraphQL client-side application, to execute queries/mutations/subscriptions against a GraphQL server. The graphql-maven-plugin generates all the necessary code, so that a Java application can call a GraphQL server by simply calling the relevant Java method.

The plugin manages two kinds of request:

  • The Full request: it's actually a standard GraphQL request, that you can test within graphiql
  • The Partial request: you can call a java method that executes one of the queries/mutations/subscriptions defined in the schema. This java method accepts one parameter for each parameter of this query/mutation/subscription.

It manages two ways of executing the request:

  • The direct execution: you call the generated method with the GraphQL request (partial or full), and you receive the result into Java objects. This is simpler, but slower: for technical reasons, the plugin has two analyze the content of the request. And it will do that at each execution. The main reason for that is to allow proper deserialization of GraphQL interfaces and unions: the __typename is injected into the query, for all returned object, union and interface types.
  • The recommended prepared execution:
    • A GraphQLRequest object is created by the application. This allows to analyze the request only once. If you create these GraphQLRequest at application startup, then the syntax control is done once for every requests at startup. This avoids to have errors occurring later, during the app execution.
    • Each GraphQL request execution is executed from this object.
    • Note: the GraphQLRequest object has been created in the 1.6 release. The prepared object was before stored into a ObjectResponse. This ObjectResponse has been maintained when used with the withQueryResponseDef Builder method, and the code that uses will continue to work. Support for other Builder method has been removed. There is no plan yet to remove the ObjectResponse object and the withQueryResponseDef Builder method. But they should be avoided in new code.

Both kinds of requests, and both modes of execution allows to use bind parameters into your queries/mutations/subscriptions (see below for more information on that)

What does the plugin generate?

When configuring the graphql-maven-plugin in client mode, it reads a GraphQL schema file, and generates all the necessary code to make it easy to call a GraphQL server.

As an overview, it generates:

  • One Executor class for each Query/Mutation/Subscription object. These Executors contain all the methods that allow to execute a full query, and shortcut methods to execute the queries, mutations and subscriptions.
    • The introspection queries (__schema and __type) are added to the query defined in the GraphQL schema. For "memory", you must provide a query in every GraphQL schema.
  • One POJO for each standard object of the GraphQL object
  • One GraphQLRequest object, that allows to store prepared queries
  • All the necessary runtime is actually attached as source code into your project: the generated code is stand-alone.
    • So, your project, when it runs, doesn't depend on any external dependency from graphql-java-generator.
      • This is why we call it an accelerator: you can generate the code once, and get rid of graphql-java-generator if you wish. BTW: we think its better to continue using it! This allows you to benefit from the enhancements the come next. But you're free to leave, and keep the generated code. :)
  • You can change this default behavior, and use the runtime into an external dependency

Creating a GraphQL client app

Please note that all the code below is taken from the Forum sample, available in the graphql-maven-plugin-samples-Forum-client sample. This module is both a sample, and a project to execute integration tests of the plugin (there is a client and a server projects, to test both side of GraphQL).

You can access directly to this module with github in the plugin Maven git or in the plugin Gradle git.

Maven or Gradle configuration

First, you'll have to create or get your GraphQL schema. The GraphQL plugin expects a .graphqls file. See the GraphQL schema doc for all the details.

Then, add the plugin either to your POM file (if you're using Maven) or your build.gradle file (if you're using Gradle):

The POM file looks like this:

<project ...>
...
        <properties>
                <graphql-maven-plugin.version>1.12.3</graphql-maven-plugin.version>
        </properties>
...
        <build>
                <plugins>
...
                        <plugin>
                                <!-- Just to be sure that your code is based on java 8 or higher -->
                                <groupId>org.apache.maven.plugins</groupId>
                                <artifactId>maven-compiler-plugin</artifactId>
                                <configuration>
                                        <source>1.8</source>
                                        <target>1.8</target>
                                        <release>8</release>
                                </configuration>
                        </plugin>
                        <plugin>
                                <groupId>com.graphql-java-generator</groupId>
                                <artifactId>graphql-maven-plugin</artifactId>
                                <version>${graphql-maven-plugin.version}</version>
                                <executions>
                                        <execution>
                                                <goals>
                                                        <goal>generateClientCode</goal>
                                                </goals>
                                        </execution>
                                </executions>
                                <configuration>
                                        <packageName>my.target.package</packageName>
                                        <customScalars>
                                                <customScalar>
                                                        <graphQLTypeName>Date</graphQLTypeName>
                                                        <javaType>java.util.Date</javaType>
                                                        <graphQLScalarTypeStaticField>com.graphql_java_generator.customscalars.GraphQLScalarTypeDate.Date</graphQLScalarTypeStaticField>
                                                </customScalar>
                                        </customScalars>
                                        <!-- The parameters below change the 1.x default behavior. They are set to respect the default behavior of the future 2.x versions -->
                                        <generateDeprecatedRequestResponse>false</generateDeprecatedRequestResponse>
                                        <separateUtilityClasses>true</separateUtilityClasses>
                                        <!-- You can add here other plugin parameters -->
                                </configuration>
                        </plugin>
                        <plugin>
                                <!-- This helps by adding the generated source in the build path of your IDE -->
                                <groupId>org.codehaus.mojo</groupId>
                                <artifactId>build-helper-maven-plugin</artifactId>
                                <executions>
                                        <execution>
                                                <id>add-source</id>
                                                <phase>generate-sources</phase>
                                                <goals>
                                                        <goal>add-source</goal>
                                                </goals>
                                                <configuration>
                                                        <sources>
                                                                <source>Z:\target\checkout\target/generated-sources/graphql-maven-plugin</source>
                                                        </sources>
                                                </configuration>
                                        </execution>
                                </executions>
                        </plugin>
...
                        <extensions>
                                <!-- Adding these extensions prevents the error below, with JDK 9 and higher: -->
                                <!-- NoSuchMethodError: 'java.lang.String javax.annotation.Resource.lookup()' -->
                                <extension>
                                        <groupId>javax.annotation</groupId>
                                        <artifactId>javax.annotation-api</artifactId>
                                        <version>1.3.2</version>
                                </extension>
                                <extension>
                                        <groupId>javax.annotation</groupId>
                                        <artifactId>jsr250-api</artifactId>
                                        <version>1.0</version>
                                </extension>
                        </extensions>
        
                </plugins>
        </build>
...
        <dependencies>
                <!-- Dependencies for GraphQL -->
                <dependency>
                        <groupId>com.graphql-java-generator</groupId>
                        <artifactId>graphql-java-runtime</artifactId>
                        <version>${graphql-maven-plugin.version}</version>
                </dependency>
...
        </dependencies>
...
</project>

The build.gradle file looks like this:

plugins {
        id "com.graphql_java_generator.graphql-gradle-plugin" version "1.12.3"
        id 'java'
}

repositories {
        jcenter()
        mavenCentral()
}

dependencies {
        // THE VERSION MUST BE THE SAME AS THE PLUGIN's ONE
        implementation "com.graphql-java-generator:graphql-java-runtime:1.12.3"
}

// The line below makes the GraphQL plugin be executed before Java compiles, so that all sources are generated on time
compileJava.dependsOn generateClientCode

// The line below adds the generated sources as a java source folder
sourceSets.main.java.srcDirs += '/build/generated/graphql-maven-plugin'

// Let's configure the GraphQL Gradle Plugin:
// All available parameters are described here: 
// https://graphql-maven-plugin-project.graphql-java-generator.com/graphql-maven-plugin/generateClientCode-mojo.html
generateClientCodeConf {
        packageName = 'org.forum.client'
        customScalars = [ [
                        graphQLTypeName: "Date",
                        javaType: "java.util.Date",
                        graphQLScalarTypeStaticField: "com.graphql_java_generator.customscalars.GraphQLScalarTypeDate.Date"
        ] ]

        // The parameters below change the 1.x default behavior. They are set to respect the default behavior of the future 2.x versions 
        generateDeprecatedRequestResponse = false
        separateUtilityClasses = true
        
        // You can add here other plugin parameters
}

You can define the package that will contain the generated code. If you don't, the default package is com.generated.graphql.

The necessary runtime source code is joined into the generated code, and remains in its original package, which is com.graphql_java_generator.*. So everything is embedded. Read the Howto personalize the generated code if you want to change this default behavior.

First build

The first build is important, before starting coding. It generates all the POJO, based on the GraphQL schema, and all the utility files.

For Maven, do:

mvn clean install

For Gradle, do:

gradle clean build

Once you've done that, the generated code is available in target/generated-sources/graphql-maven-plugin. Thanks to the build-helper-maven-plugin, this folder should be available as a source folder in your IDE.

The minimal app

We can now create a first app. As we're in a Spring mode, we're using Spring Boot.

package com.graphql_java_generator.minimal_app;

import java.util.Date;

import com.graphql_java_generator.exception.GraphQLRequestExecutionException;
import com.graphql_java_generator.exception.GraphQLRequestPreparationException;
import com.graphql_java_generator.samples.forum.client.graphql.forum.client.QueryTypeExecutor;

public class MinimalNonSpringApp {

        public static void main(String[] args) throws GraphQLRequestExecutionException, GraphQLRequestPreparationException {
                // The executor, that allows to execute GraphQL queries. The class name is the one defined in the GraphQL schema.
                // You can instanciate in the same way the mutation and the subscription executors.
                QueryTypeExecutor queryExecutor = new QueryTypeExecutor("http://localhost:8180/graphql");

                // A basic demo of input parameters
                Date date = new Date(2019 - 1900, 12 - 1, 20);

                // For this simple sample, we execute a direct query. But prepared queries are recommended.
                // Please note that input parameters are mandatory for list or input types.
                System.out.println(
                                "Executing query: '{id name publiclyAvailable topics(since: &param){id}}', with input parameter param of value '"
                                                + date + "'");
                System.out
                                .println(queryExecutor.boards("{id name publiclyAvailable topics(since: &param){id}}", "param", date));
        }
}

For simplicity, this class contains only the main method. Here is what it does:

  • Instanciate a query executor. The class name is the name of the query, as defined in the GraphQL schema.
    • You can instanciate in the same way the mutation and the subscription executors.
  • Initializes a date variable, to show the use of input parameters.
  • Uses the queryExecutor to execute a boards query. The name of the query is the one defined in the GraphQL schema.
  • This sample provides the input parameter value (or values, as query/mutation/subscription may contain more than one input parameters), from this couple if method parameters:
    • The name of the parameter, as defined in the query string. Here: param
    • The runtime value of the parameter.