-
Notifications
You must be signed in to change notification settings - Fork 48
client_graphql_repository
Starting with release 1.18, you can use GraphQL Repositories.
GraphQL Repositories is an adaptation of Spring Repositories, for GraphQL request.
The idea is to write no code at all for the GraphQL request execution: just create a Java interface that defines the GraphQL queries, mutations and subscriptions you need. The runtime will take care of all the execution stuff.
Here is a sample:
import org.allGraphQLCases.client.Character;
import org.allGraphQLCases.client.Human;
import org.allGraphQLCases.client.HumanInput;
import com.graphql_java_generator.annotation.RequestType;
import com.graphql_java_generator.client.graphqlrepository.BindParameter;
import com.graphql_java_generator.client.graphqlrepository.FullRequest;
import com.graphql_java_generator.client.graphqlrepository.GraphQLRepository;
import com.graphql_java_generator.client.graphqlrepository.PartialRequest;
import com.graphql_java_generator.exception.GraphQLRequestExecutionException;
@GraphQLRepository
public interface MyGraphQLRepository extends PartialQueries {
@PartialRequest(request = "{appearsIn name}")
List<Character> withoutParameters() throws GraphQLRequestExecutionException;
@PartialRequest(request = "{id name}", requestType = RequestType.mutation)
Human createHuman(HumanInput human) throws GraphQLRequestExecutionException;
@FullRequest(request = "subscription {subscribeToAList {}}", requestType = RequestType.subscription)
SubscriptionClient subscribeToAList(SubscriptionCallback<?> callback) throws GraphQLRequestExecutionException;
}
This GraphQL Repository can be used as described below, in a Spring application:
@Component
public class MyCode {
@Autowired
MyGraphQLRepository myRepo;
public void doSomething() throws Exception {
List<Character> chars = myRepo.withoutParameters();
// Do something with chars
}
}
```
And that's all: you don't write any boilerplate code for the GraphQL request execution.
__Importante notices__:
* When in Spring app (like in the sample above), you must enable the GraphQL Repositories in the Spring Context. See below how to use GraphQL Repositories in Spring apps.
* When in non-Spring app, you'll have to copy/paste two lines of codes to build the dynamic proxy that will execute the GraphQL queries. See below how to use GraphQL Repositories in non-Spring apps.
# Rules to respect, to define a GraphQL Repository
You may define as may GraphQL Repositories as you want:
* For Spring app, they all will be discovered provided that their package (or parent package) are provided in the `@EnableGraphQLRepositories` annotation
* For non-Spring app, the proxy must be created by a call to `Proxy.newProxyInstance(..)` (see below for the details)
A GraphQL Repository __may__:
* Mix partial requests and full requests
* Mix queries, mutations and subscriptions
A GraphQL Repository __must__:
* Contain only GraphQL methods, that is: its method must be annotated by either `@PartialRequest` or `@FullRequest`:
* The `@PartialRequest` allows these parameters (see below for details):
* requestName: optional if the method name is exactly the same as the GraphQL query/mutation/subscription name in the GraphQL schema. Mandatory if the method name is different.
* request: the request string, as explained for Partial Request in the [[client execution|client_exec_graphql_requests]] page. This request may contain Bind Parameters.
* requestType: optional if the request is a query. Mandatory otherwise. It contains one of the item of the `RequestType` enumeration (query, mutation or subscription)
* The `@FullRequest` annotation allows these parameters:
* request: the request string, as explained for Partial Request in the [[client execution|client_exec_graphql_requests]] page. This request may contain Bind Parameters.
* requestType: optional if the request is a query. Mandatory otherwise. It contains one of the item of the `RequestType` enumeration (query, mutation or subscription)
Each method of a GraphQL Repository __must__ respect these rules:
* It returns either the same type as defined in the GraphQL schema (for query and mutation) or a `SubscriptionClient` (for subscription):
* For Partial Request query or mutation, it's the type of the query or mutation field
* For Partial Request subscription, it's a `SubscriptionClient`
* For Full Request query or mutation, it's the query or mutation type.
* For Full Request subscription, it's a `SubscriptionClient` (the full request may then contain only one subscription)
* For Partial Requests only:
* The first parameters must be the parameters of the specified query or mutation, in the exact same order as defined in the GraphQL schema, and with not java annotation (more info later on this page).
* Their type must be the type defined in the GraphQL schema, for the relevant parameter.
* Please take care to these points :
* The `Float` GraphQL type maps to the `Double` java type (not the Float java type)
* The return type of a method may not be the java scalar type (int, double, boolean). It must be the class type (Integer, Double, Boolean).
* (Optionally) The method defines one parameter for each Bind Parameter or GraphQL variable that is defined in the provided GraphQL request (see the [[client execution|client_exec_graphql_requests]] page for more information on Bind Parameters and GraphQL variables)
* These method parameters must be marked by the `@BindParameter` java annotation (see below)
* It throws exactly one exception: `GraphQLRequestExecutionException`
# Define your GraphQL Repository
## Prerequisite for Spring
The first step is to activate the GraphQL capability. To do this, you have to annotate a Spring Configuration class with the `EnableGraphQLRepositories` annotation:
```java
import org.springframework.context.annotation.Configuration;
import com.graphql_java_generator.client.graphqlrepository.EnableGraphQLRepositories;
@Configuration
@EnableGraphQLRepositories({ "org.allGraphQLCases.demo.impl", "org.allGraphQLCases.subscription.graphqlrepository" })
public class SpringTestConfig {
}
```
The parameter for this annotation is the list of package names where Spring should look for GraphQL Repositories.
The GraphQL Repositories are interfaces annotated with the `GraphQLRepository` annotation, like this:
```java
import com.graphql_java_generator.client.graphqlrepository.GraphQLRepository;
@GraphQLRepository
public interface FullRequestSubscriptionGraphQLRepository {
... Write GraphQL requests here
}
```
Note: if Spring doesn't find your GraphQL Repositories, check the `EnableGraphQLRepositories` annotation, and the provided package names.
## Prerequisite for non-Spring app
When in non-Spring application, the automagic Spring discovery doesn't work. It remain simple to use the GraphQL Repository. You create a proxy class for each of you GraphQL Repositories this way:
```java
import java.lang.reflect.Proxy;
..
// If MyGraphQLRepository is your GraphQL Repository interface :
GraphQLRepositoryInvocationHandler<MyGraphQLRepository> invocationHandler =
new GraphQLRepositoryInvocationHandler<MyGraphQLRepository>(MyGraphQLRepository.class,
queryExecutor, mutationExecutor, subscriptionExecutor);
MyGraphQLRepository myGraphQLRepository =
(MyGraphQLRepository) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] {MyGraphQLRepository.class}, invocationHandler);
// You can then execute GraphQL by using the created proxy:
MyGraphQLType response = myGraphQLRepository.myGraphQLRequest(param1, param2);
```
## Define Full Requests in a GraphQL Repository
Full Request are easy to define.
When defining a method that is mapped to a GraphQL Full Request (that is: a regular GraphQL query, as you can test it with graphiql), it must respect these conditions:
* The method must be annotated by the `@FullRequest` java annotation. This annotation has these parameters:
* The GraphQL request string, in the `request` parameter. This request is typically a copy/paste of a request tested with graphiql, with possibly Bind Parameters and GraphQL variables (see the [[client execution page|client_exec_graphql_requests]] for more information on this)
* The GraphQL request type, in the `requestType` parameter. This parameter is optional, and may be omitted if your request is a query one.
* _(for query and mutation)_ The request may contain any number of queries or mutations
* _(for subscription only)_ The request may contain only one subscription. This is because you must provide its callback, and the method returns the `SubscriptionClient` for this subscription.
* The method must have one parameter per Bind Parameter and GraphQL variable defined in the request query
* _(for subscription only)_ The first parameter must be a `SubscriptionCallback<Xxxx>`, where _Xxxx_ is the type of the subscription field, that is: the GraphQL type that will be sent for each subscription notification.
* You can then add any number of method parameters. These parameters must map to a Bind Parameter or GraphQL variable. As such, they must be annotated with the `@BindParameter` java annotation.
* This annotation has one parameter: `name` must contain the name of the Bind Parameter or GraphQL variable, as defined in the request string.
* No other method parameter is allowed
* _(for query and mutation)_ The method must return the query type or the mutation type, as defined in the GraphQL schema.
* It's then up to you to retrieve the field value(s) that map(s) to the query(ies) or mutation(s) that was in the request string
* _(for subscription only)_ The method must return a `SubscriptionClient`, that mainly allows to close the subscription, and properly clean the client and server resources.
* The method must throw the `GraphQLRequestExecutionException`.
Here is a sample of a GraphQL Repository, with Full Requests:
```java
@GraphQLRepository
public interface GraphQLRepositoryTestCase {
/** Full request: query */
@FullRequest(request = "{directiveOnQuery (uppercase: ?uppercase) @testDirective(value:&valueParam)}")
public MyQueryType fullRequest1(@BindParameter(name = "uppercase") boolean uppercase,
@BindParameter(name = "valueParam") String value) throws GraphQLRequestExecutionException;
/** Full request: query, with queryType */
@FullRequest(request = "{directiveOnQuery (uppercase: ?uppercase) @testDirective(value:&valueParam)}", requestType = RequestType.query)
public MyQueryType fullRequest2(@BindParameter(name = "valueParam") String value,
@BindParameter(name = "uppercase") boolean uppercase) throws GraphQLRequestExecutionException;
/** Full request: mutation */
@FullRequest(request = "mutation($input: HumanInput!) {createHuman(human: $input) {id name }}", requestType = RequestType.mutation)
public AnotherMutationType fullRequestMutation(@BindParameter(name = "input") HumanInput humanInput)
throws GraphQLRequestExecutionException;
/** Full request: subscription */
@FullRequest(request = "subscription {issue53(date: &date) {}}", requestType = RequestType.subscription)
public SubscriptionClient fullSubscription(SubscriptionCallback<Date> callback,
@BindParameter(name = "date") Date date) throws GraphQLRequestExecutionException;
// You may add any number of GraphQL requests, including Partial Requests
}
```
## Define Partial Requests in a GraphQL Repository
The short story: with Partial Requests, you need to take care of the parameter for the choosed query, mutation or subscription. Anything else is the same as the Full Request, out of the Java annotation.
When defining a method that is mapped to a GraphQL Partial Request, it must respect these conditions:
* The method must be annotated by the `@PartialRequest` java annotation. This annotation has these parameters:
* requestName: the GraphQL request name.
* It's optional if the method name is the field name for the query, mutation or subscription, as defined in the GraphQL query type, mutation type or subscription type
* request: the GraphQL request string. It may contain Bind Parameters (see the [[client execution page|client_exec_graphql_requests]] for more information on this).
* Please note the Partial Requests may not contain GraphQL variables
* requestType: the GraphQL request type. This parameter is optional, and may be omitted if your request is a query one.
* The method must have these parameters:
* _(for subscription only)_ The first parameter must be a `SubscriptionCallback<Xxxx>`, where _Xxxx_ is the type of the subscription field, that is: the GraphQL type that will be received for each subscription notification.
* You must provide the exact same method parameters as the field parameters for the query, mutation or subscription field, as defined in the GraphQL schema
* For instance, for this `query1` query: `Query {query1(param1: Int!, param2: MyInputType): MyGraphQLType}`, the GraphQL Repository java method should like this: `public MyGraphQLType query1(Integer p1, MyInputType p2)`, where the method name is free if you used the requestName parameter of the `@PartialRequest` java annotation.
* These parameters __must not__ be annotated by the `@BindParameter` java annotation.
* Of course, of your query, mutation or subscription has no parameter, then the java method will have no such parameter.
* You can then add any number of method parameters. Each of these parameters must map to a Bind Parameter. As such, they must be annotated with the `@BindParameter` java annotation.
* This annotation has one parameter (`name`) which must contain the name of the Bind Parameter, as defined in the request string.
* No other method parameter is allowed
* _(for query and mutation)_ The method must return the query type or the mutation type, as defined in the GraphQL schema.
* It's then up to you to retrieve the field value(s) that map(s) to the query(ies) or mutation(s) that was in the request string
* _(for subscription only)_ The method must return a `SubscriptionClient`, that mainly allows to close the subscription, and properly clean the client and server resources.
* The method must throw the `GraphQLRequestExecutionException`.
Here is a sample of a GraphQL Repository, with Partial Requests:
```java
@GraphQLRepository
public interface GraphQLRepositoryTestCase {
// withOneOptionalParam is a field of the Query GraphQL type
@PartialRequest(request = "{appearsIn name}")
public Character withOneOptionalParam(CharacterInput character) throws GraphQLRequestExecutionException;
@PartialRequest(requestName = "withOneOptionalParam", request = "{appearsIn name id}")
public Character thisIsNotARequestName1(CharacterInput character) throws GraphQLRequestExecutionException;
// A sample with one query parameter (input) and two Bind Parameters.
@PartialRequest(requestName = "allFieldCases", request = "{listWithoutIdSubTypes(nbItems: &nbItemsParam, input:?fieldParameterInput)}")
public AllFieldCases thisIsNotARequestName3(AllFieldCasesInput input,
@BindParameter(name = "nbItemsParam") long nbItems, //
@BindParameter(name = "fieldParameterInput") FieldParameterInput fieldParameterInput)
throws GraphQLRequestExecutionException;
// A mutation sample (note the requestType annotation parameter)
// Of course, it could also have some Bind Parameters
@PartialRequest(request = "{id}", requestType = RequestType.mutation)
public Human createHuman(HumanInput character) throws GraphQLRequestExecutionException;
// A subscription sample (note the requestType annotation parameter)
// Of course, it could also have some Bind Parameters
@PartialRequest(request = "{ id appearsIn }", requestType = RequestType.subscription)
public SubscriptionClient subscribeNewHumanForEpisode(SubscriptionCallback<Human> subscriptionCallback,
Episode episode) throws GraphQLRequestExecutionException;
// You can any number of other GraphQL request methods, including Full Requests
}
```
# Execute GraphQL requests defined in a GraphQL Repository
## In a Spring app
Like with Spring Repositories, the GraphQL Repositories needs to be wired into the Spring Context. To do this, you must add the `EnableGraphQLRepositories` java annotation on a Spring Configuration class.
Here is a sample:
```java
[...]
import com.graphql_java_generator.client.graphqlrepository.EnableGraphQLRepositories;
@Configuration
@ComponentScan(basePackageClasses = { GraphQLConfiguration.class, QueryExecutor.class })
@EnableGraphQLRepositories({ "org.allGraphQLCases.demo.impl", "org.allGraphQLCases.subscription.graphqlrepository" })
public class SpringTestConfig {
}
```
Once you've done this, Spring will create a dynamic proxy for each interface marked with the `@GraphQLRepositoryTestCase` annotation.
So if the `org.allGraphQLCases.demo.impl` package contains this interface:
```java
package org.allGraphQLCases.demo.impl;
@GraphQLRepository
public interface MyGraphQLRepository {
// This interface contains several method marked either by @PartialRequest or @FullRequest
}
```
Then it can be used this way, in any Spring component:
```java
@Component
public class MyComponent {
@Autowired
MyGraphQLRepository myGraphQLRepo;
public void doSomething() throws Exception {
MyGraphQLType response = myGraphQLRepo.executeMyGraphQLRequest(myparams);
[Do something with this result]
}
}
```
__Important Notice__: The above class must be a Spring bean (component...), so that Spring autowires the MyGraphQLRepository proxy into the `myGraphQLRepo` attribute.
## In a non-Spring app
In a non-Spring app, it's up to you to call the method that creates the dynamic proxy. To do this, it's just a matter copying/pasting two lines of code.
Considering that you've defined this GraphQL Repository:
```java
package org.allGraphQLCases.demo.impl;
@GraphQLRepository
public interface MyGraphQLRepository {
// This interface contains several method marked either by @PartialRequest or @FullRequest
}
```
You can create the dynamic proxy with these lines:
```java
public class MyUsefulClass {
private MyGraphQLRepository myGraphQLRepo;
public MyUsefulClass(String endpoint) {
QueryExecutor queryExecutor = new QueryExecutor(endpoint);
MutationExecutor mutationExecutor = new MutationExecutor(endpoint);
SubscriptionExecutor subscriptionExecutor = new SubscriptionExecutor(endpoint);
//Step 1: create the invocationHandler
GraphQLRepositoryInvocationHandler<MyGraphQLRepository> invocationHandler = new GraphQLRepositoryInvocationHandler<MyGraphQLRepository>
(MyGraphQLRepository.class, queryExecutor, mutationExecutor, subscriptionExecutor);
//Step 2: create the dynamic proxy
myGraphQLRepo = (MyGraphQLRepository) Proxy.newProxyInstance(App.class.getClassLoader(),
new Class[] { MyGraphQLRepository.class }, invocationHandler);
}
public void doSomething() throws Exception {
MyGraphQLType response = myGraphQLRepo.executeMyGraphQLRequest(myparams);
[Do something with this result]
}
}
```
# Using GraphQL Repositories with Spring againsy several GraphQL servers
In this case, two additional rules apply:
* One GraphQL Repository may contain requests (queries, mutations and/or subscriptions) based on only one unique GraphQL schema
* The `QueryExecutor` of this GraphQL schema must be provided in the `queryExecutor` parameter of the `GraphQLRepository` java annotation
Here is a sample:
```java
@GraphQLRepository(queryExecutor = QueryExecutor.class)
public interface PartialRequestGraphQLRepository extends PartialQueries {
... Your Request definitions go here
}
```
As `QueryExecutor` is the query executor for the _Forum_ schema, all queries, mutations and/or subscriptions of this GraphQL Repositories must be based on this GraphQL schema.
Creating a first app (non spring)
Connect to more than one GraphQL servers
Easily execute GraphQL requests with GraphQL Repositories
Access to an OAuth2 GraphQL server
How to personalize the client app
Howto personalize the generated code
Client migration from 1.x to 2.x
Implement an OAuth2 GraphQL server
Howto personalize the generated code
Server migration from 1.x to 2.x