Skip to content

client_subscription

etienne-sf edited this page Jun 22, 2024 · 23 revisions

Subscription in client mode

The client mode makes it easy for a Java GraphQL client-side application, to execute 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.

Since v1.14, subscriptions work with both Partial Request and Full Requests. You'll find the differences between Full and Partial requests on the client FAQ and the client page. Since 2.3, subscriptions can be executed in reactive mode. That is: the reactive executor methods return:

  • A Flux of the Subscription type (for full requests)
  • A Flux of the subscribed field's type (for partial requests)

As for queries and mutations, subscription can be executed in two ways:

  • 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.
    • As Subscription is usually executed once, the direct execution is the recommended way to execute subscriptions.
  • The prepared execution:
    • A GraphQLRequest object is created by the application, if possible at application startup. This allows to analyze the request only once. And it avoids errors during the app execution, as all the GraphQL requests have been checked at startup.
    • Each GraphQL request execution is executed from this object.

Both kinds of requests, and both modes of execution allows to use bind parameters into your query definitions.

How to use subscriptions with the plugin?

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 to your Maven POM file, as described in the client page.

Full requests

Non reactive executor

Since v1.14, you can execute subscriptions with full requests. The main advantage is that it allows you to use GraphQL variables.

Here is a sample:

@Component
public class SubscriptionRequests {

	@Autowired
	SubscriptionExecutor subscriptionExecutor;
	
	GraphQLRequest subscriptionRequest;
	
	// Let's prepare the request, once the bean is ready (and the autowired attributes have been set by Spring)
	@PostConstruct
	public void init() {
		subscriptionRequest = subscriptionExecutor.getGraphQLRequest(
				"subscription sub($boardNameParam: String!) {subscribeToNewPost(boardName: $boardNameParam){}}");
	}

	public SubscriptionClient execSubscription(SubscriptionCallback<Post> callback, String boardName)
			throws GraphQLRequestPreparationException, GraphQLRequestExecutionException, IOException {
		
		// Execution of the full request, with one GraphQL Variable: 
		// * The callback that will receive the notification (and errors, if any occurs during subscription execution).
		// * As this subscription returns Post instances, it must be an implementation of SubscriptionCallback<Post> 
		// * The boardNameParam (as defined in the above query): you can provide the list of parameters, by pair: the parameter's name then its value, here: "boardNameParam", boardName
		return subscriptionExecutor.exec(callback, "boardNameParam", boardName);
	}
}

The callback is an instance of [SubscriptionCallback<Post>](https://github.com/graphql-java-generator/graphql-maven-plugin-project/blob/master/graphql-java-client-runtime/src/main/java/com/graphql_java_generator/client/SubscriptionCallback.java). This callback will receive all the notifications for this subscription, especially:

  • onMessage(T t), will be called for each received message from the subscription. In our sample, T is a Post
  • onClose(int statusCode, String reason) when the subscription is closed
  • onError(Throwable cause) when an error occurs

Reactive executor

Since v2.3, you can execute subscriptions with reactive requests.

Here is a sample:

@Component
public class SubscriptionRequests {

	@Autowired
	SubscriptionExecutor subscriptionExecutor;
	
	GraphQLRequest subscriptionRequest;
	
	// Let's prepare the request, once the bean is ready (and the autowired attributes have been set by Spring)
	@PostConstruct
	public void init() {
		subscriptionRequest = subscriptionExecutor.getGraphQLRequest(
				"subscription sub($boardNameParam: String!) {subscribeToNewPost(boardName: $boardNameParam){}}");
	}

	public Flux<Subscription> execSubscription(String boardName) {
		
		// As this is a full request, it return a Flux of the GraphQL Subscription object
		// The boardNameParam (as defined in the above query): you can provide the list of parameters, by pair: the parameter's name then its value, here: "boardNameParam", boardName
		return subscriptionExecutor.exec(subscriptionRequest, "boardNameParam", boardName);
	}
}

Partial requests

Non reactive executor

In each query/mutation/subscription class, the plugin also generates Xxxx and XxxxWithBindValues methods, where Xxxx is successively each field defined in this query/mutation/subscription object.

In these methods:

  • The query/mutation/subscription parameters (as defined in the GraphQL schema) become parameters of the relevant generated java methods. So you don't need to define and map bind parameters for them.
    • But you can still use bind parameters for input parameters of the fields you request, of course
  • The subscription methods return an instance of SubscriptionClient.
    • This interface has only one method: unsubscribe(), which allows to unsubscribe from this subscribed subscription. This frees resources on both the client and the server, and the network of course.

Below is a sample of the client code:

@Component
public class SubscriptionRequests {

	@Autowired
	SubscriptionExecutor subscriptionExecutor;
	
	GraphQLRequest subscriptionRequest;
	
	// Let's prepare the request, once the bean is ready (and the autowired attributes have been set by Spring)
	@PostConstruct
	public void init() {
		subscriptionRequest = subscriptionExecutor.getSubscribeToNewPostGraphQLRequest
			("{id date author publiclyAvailable title content}");
	}

	public SubscriptionClient execSubscription(SubscriptionCallback<Post> callback)
			throws GraphQLRequestPreparationException, GraphQLRequestExecutionException, IOException {
		
		// Execution of a partial request. The subscribeToNewPost has one parameter: boardName. This parameter is
		// expected from the subscribeToNewPost generated in the executor, as this is a partial request.
		return subscriptionExecutor.subscribeToNewPost(subscriptionRequest, callback, "Board name 1");
	}
}

Reactive executor

Below is a sample of the client code:

import java.util.Optional;
import reactor.core.publisher.Flux;

@Component
public class SubscriptionRequests {

	@Autowired
	SubscriptionExecutor subscriptionExecutor;
	
	GraphQLRequest subscriptionRequest;
	
	// Let's prepare the request, once the bean is ready (and the autowired attributes have been set by Spring)
	@PostConstruct
	public void init() {
		subscriptionRequest = subscriptionExecutor.getSubscribeToNewPostGraphQLRequest
			("{id date author publiclyAvailable title content}");
	}

	public Flux<Optional<Post>> execSubscription() {
		
		// Execution of a partial request. The subscribeToNewPost has one parameter: boardName. This parameter is
		// expected from the subscribeToNewPost generated in the executor, as this is a partial request.
		return subscriptionExecutor.subscribeToNewPost(subscriptionRequest, "Board name 1");
	}
}

Use direct queries

Non reactive executor

If you don't want to store the GraphQLRequest, you can avoid that, by using direct queries. The overhead that exists at each execution is not an issue here, as you'll probably execute the subscription only once.

You still need to provide the callback, that'll receive the notifications you've subscribed to.

Here is a sample with the bind parameters value given as method parameters:

@Component
public class SubscriptionRequests {

	@Autowired
	SubscriptionExecutor subscriptionExecutor;

	public SubscriptionClient subscribe(PostSubscriptionCallback<Post> postSubscriptionCallback) {
		// The subscription request accepts one parameter. If you don't want to provide it, you can give it the null value
		return subscriptionExecutor.subscribeToNewPost("{id date author publiclyAvailable title content}", postSubscriptionCallback, "Board name 1");
	}
}

Reactive executor

Here is a sample with the bind parameters value given as method parameters:

import java.util.Optional;
import reactor.core.publisher.Flux;

@Component
public class SubscriptionRequests {

	@Autowired
	SubscriptionExecutor subscriptionExecutor;

	public Flux<Optional<Post>> subscribe() {
		// The subscription request accepts one parameter. If you don't want to provide it, you can give it the null value
		return subscriptionExecutor.subscribeToNewPost("{id date author publiclyAvailable title content}", postSubscriptionCallback, "Board name 1");
	}
}
Clone this wiki locally