Skip to content

client_non_spring

etienne-sf edited this page Aug 28, 2024 · 53 revisions

Introduction

Since the 1.12 release, the recommended way to build a client with this plugin, is to use it as Spring app. It adds a little latency when starting the app, but it allows all the "magic" of Spring. You can keep your own code to be "non spring", as described in the client FAQ.

This page describes how to use the GraphQL from a jersey http client that you provide to the plugin, or that the plugin will create.

Doing this makes it more difficult to use some complex http configurations (https, cookies...). And very difficult to authenticate against a GraphQL server that would use OAuth, SAML, OpenID Connect... This last point is the main reason that leads to use Spring and its up-to-date Spring Security module. On the contrary, authenticate a Spring Boot application against an OAuth2 protected server is very easy. You can take a look at the OAuth client page page: just add 5 lines of code and some configuration lines, and it's done!

Use the Spring capabilities with a non spring app

You can use the Spring capabilities with a non spring app. Please check this FAQ for more information on this: Use the Spring capabilities with a non-spring App.

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:

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 to 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 must be injected into the query, for all returned object, union and interface types, in order to properly deserialize the interfaces and union into the proper class.
  • 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 java interface for each GraphQL union and interface
    • One java class for each GraphQL type and input type, including the query, mutation and subscription (if any). If the GraphQL type implements an interface, then its java class implements this same interface
    • One java enum for each GraphQL enum
  • One GraphQLRequest object, that allows to store prepared queries
  • You can adapt the plugin behavior to your needs. Take a tour on the Client personalization page for all the information

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, provided here. 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 sides of GraphQL).

You can access directly to this sample with github in the Maven project or in the Gradle project.

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>2.8</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>${project.build.directory}/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-client-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 "2.8"
	id 'java'
}

repositories {
	jcenter()
	mavenCentral()
}

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

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

// The line below adds the generated sources as a java source folder in the IDE
sourceSets.main.java.srcDirs += '/build/generated/sources/graphqlGradlePlugin'
sourceSets.main.java.srcDirs += '/build/generated/resources/graphqlGradlePlugin'

// 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 plugin 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:

  • For Maven: 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.
  • For Gradle: in the /build/generated/sources/graphqlGradlePlugin (for Gradle, since 1.15), thanks to the sourceSets.main.java.srcDirs[..] line in the build.gradle file

The minimal app

We can now create a first app. It can looks like this:

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.QueryExecutor;

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.
		QueryExecutor queryExecutor = new QueryExecutor("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.
Clone this wiki locally