Dynamic client introduction and basic usage
A Java GraphQL client. The main difference from the typesafe client is
that while the typesafe client behaves like a typesafe proxy very
similar to the MicroProfile REST Client, the dynamic client is more like
the JAX-RS client from the jakarta.ws.rs.client
package. Instead of
working with model classes directly, the dynamic client focuses on
programmatically working with GraphQL documents representing GraphQL
requests and responses. It still offers the option to convert between
documents and model classes when necessary.
In the current implementation, Vert.x HTTP client is used for handling the underlying traffic.
Creating a client instance
Generally there are two ways to obtain a client instance.
First, using CDI injection where the configuration values are defined in system properties:
@Inject
@GraphQLClient("superheroes")
DynamicGraphQLClient client;
// assuming that this system property exists:
// superheroes/mp-graphql/url=https://superheroes.org/graphql
The above example assumes that configuration for the client is present in system properties. For a full list of supported configuration properties, see Client configuration reference
The other way to build a client is programmatically using a builder:
DynamicGraphQLClient client = DynamicGraphQLClientBuilder.newBuilder()
.url("https://superheroes.org/graphql")
.build();
The usage examples in the following sections will assume using the first approach - injection.
Basic Usage
Given the following GraphQL service on the server side:
@GraphQLApi
class SuperHeroesApi {
@Query
List<SuperHero> allHeroesIn(String location) {
// ....
}
}
class SuperHero {
private String name;
private List<String> superPowers;
}
Such service can be queried this way:
package examples.dynamicclient;
import examples.typesafeclient.SuperHero;
import io.smallrye.graphql.client.GraphQLClient;
import io.smallrye.graphql.client.Response;
import io.smallrye.graphql.client.core.Document;
import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient;
import jakarta.inject.Inject;
import jakarta.json.JsonArray;
import java.util.List;
import java.util.concurrent.ExecutionException;
import static io.smallrye.graphql.client.core.Argument.arg;
import static io.smallrye.graphql.client.core.Argument.args;
import static io.smallrye.graphql.client.core.Document.document;
import static io.smallrye.graphql.client.core.Field.field;
import static io.smallrye.graphql.client.core.Operation.operation;
public class MyClientUsage {
@Inject
@GraphQLClient("superHeroes")
DynamicGraphQLClient client;
public void execute() throws ExecutionException, InterruptedException {
Document document = document( // <1>
operation(field("allHeroesIn",
args(arg("location", "Outer Space")),
field("name"),
field("superPowers"))));
Response response = client.executeSync(document); // <2>
JsonArray heroesArray = response.getData().getJsonArray("allHeroesIn"); // <3>
List<SuperHero> heroes = response.getList(SuperHero.class, "allHeroesIn"); // <4>
}
}
-
<1> Creating the document representing the request. We used static imports to make the code easy to read, they all come from the classes in the
io.smallrye.graphql.client.core
package. -
<2> Executing the request. You can either do that in a blocking way, or request a
Uni
if you prefer the reactive style. -
<3> Obtaining the resulting list of heroes as a
JsonArray
. -
<4> Obtaining the resulting list of heroes as instances of the model class. This is optional, you can continue working with the data as a
JsonArray
if you prefer.
Using plain strings instead of the DSL
If you don't like the DSL for some reason and want to use plain strings for your queries, these two examples will serve you:
package examples.dynamicclient;
import io.smallrye.graphql.client.GraphQLClient;
import io.smallrye.graphql.client.Response;
import io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient;
import jakarta.inject.Inject;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
public class MyClientUsageString {
@Inject
@GraphQLClient("superheroes")
DynamicGraphQLClient client;
public void execute() throws ExecutionException, InterruptedException {
String queryWithInlineVariables = // <1>
"query {" +
" allHeroesIn(location: \"Outer Space\") {" +
" name" +
" superPowers" +
" }" +
"}";
Response response = client.executeSync(queryWithInlineVariables);
String queryWithExtractedVariables = // <2>
"query($loc: String) {" +
" allHeroesIn(location: $loc) {" +
" name" +
" superPowers" +
" }" +
"}";
Map<String, Object> variables = new HashMap<>(); // <3>
variables.put("loc", "Outer Space");
Response response2 = client.executeSync(queryWithExtractedVariables, variables);
}
}
-
<1>: In this variant, we inline values for query arguments directly into the query string.
-
<2>: In this variant, argument values are extracted into variables. The
location
argument of theallHeroesIn
query is declared to be using the variableloc
(the variable can also be namedlocation
same as the argument, if you prefer). -
<3>: Here we create a map that defines the values for each variable. Values of this map are
Object
s, so you can put in strings, numbers, booleans, or any object that corresponds to a GraphQL type and can be serialized to JSON. Inserting aJsonObject
directly is also supported.
Accessing HTTP headers and response codes
To access HTTP transport metadata that was passed by the server, you can inspect the Response
object.
Response.getTransportMeta("HEADER-NAME")
returns a List<String>
containing (potentially multiple) values of the requested header.
To get the HTTP status code or status message, these are stored inside the transportMeta
map too. ResponseImpl
contains convenience methods to retrieve them: ResponseImpl.getStatusCode()
and ResponseImpl.getStatusMessage()
.
It's also possible to retrieve them directly without casting to ResponseImpl
by calling
Integer.valueOf(response.getTransportMeta().get("<status-code>").get(0))
and
response.getTransportMeta().get("<status-message>").get(0)
.
HTTP headers, status codes and messages are only available for operations executed over pure HTTP, not via websockets!