-
Notifications
You must be signed in to change notification settings - Fork 50
client_faq
To use advanced features (like Spring Security, OAuth2, https personalization...) you'll need to execute the app with a Spring Context.
But you can keep your non-spring app with this context: you'll then have to start a spring app that will load the Spring Context, and the GraphQL components. Then you call your main()
entry point, and your application starts. It can then retrieve the GraphQL query/mutation/subscription executors to execute GraphQL requests.
You'll find the working samples in the Forum sample:
The NonSpringWithSpringGraphQLConfMain
is the new main()
. It's a Spring app, and it load the Spring context. You'll see at the end of the class the static getters that allows to retrieve the QueryTypeExecutor
and MutationTypeExecutor
executors:
@SpringBootApplication(scanBasePackageClasses = { MinimalSpringApp.class, GraphQLConfiguration.class,
QueryTypeExecutor.class })
public class NonSpringWithSpringGraphQLConfMain implements CommandLineRunner {
/**
* This singleton allows the static getters to retrieve the Spring components that have been autowired from Spring
*/
private static NonSpringWithSpringGraphQLConfMain nonSpringWithSpringGraphQLConfApp;
/**
* The executor, that allows to execute GraphQL queries. The class name is the one defined in the GraphQL schema.
*/
@Autowired
QueryTypeExecutor queryExecutor;
/**
* The executor, that allows to execute GraphQL mutations. The class name is the one defined in the GraphQL schema.
* It will be null if no mutation has been defined.
*/
@Autowired(required = false)
MutationTypeExecutor mutationExecutor;
/**
* The executor, that allows to execute GraphQL subscriptions. The class name is the one defined in the GraphQL
* schema. It will be null if no subscription has been defined.
*/
@Autowired(required = false)
SubscriptionTypeExecutor subscriptionExecutor;
public static void main(String[] args) {
SpringApplication.run(MinimalSpringApp.class, args);
}
/**
* This method is started by Spring, once the Spring context has been loaded. This is run, as this class implements
* {@link CommandLineRunner}
*/
@Override
public void run(String... args) throws Exception {
nonSpringWithSpringGraphQLConfApp = this;
// The Spring context is now created, including the GraphQL stuff. Let's start the non Spring app
OldMain.main(args);
}
/**
* Getter for the {@link QueryTypeExecutor} that has been loaded by Spring
*
* @return
*/
public static QueryTypeExecutor getQueryTypeExecutor() {
return nonSpringWithSpringGraphQLConfApp.queryExecutor;
}
/**
* Getter for the {@link SubscriptionTypeExecutor} that has been loaded by Spring
*
* @return
*/
public static SubscriptionTypeExecutor getSubscriptionTypeExecutor() {
return nonSpringWithSpringGraphQLConfApp.subscriptionExecutor;
}
}
The old Main class uses the static
public class OldMain {
public static void main(String[] args) throws Exception {
// A basic demo of input parameters
@SuppressWarnings("deprecation")
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: ¶m){id}}', with input parameter param of value '"
+ date + "'");
// In the below line, NonSpringWithSpringGraphQLConfApp static getter is used to retrieve the QueryExecutor
System.out.println(NonSpringWithSpringGraphQLConfMain.getQueryTypeExecutor()
.boards("{id name publiclyAvailable topics(since: ¶m){id}}", "param", date));
System.out.println("Normal end of the application");
}
}
The way to define the GraphQL server endpoint varies, depending on the kind of app you uses:
For Spring app, you'll update the application.properties
or application.yml
file, for instance:
graphql.endpoint.url = http://localhost:8180/my/updated/graphql/path
For non Spring app, you'll define the GraphQL server endpoint with one of these ways:
- Call the static
com.graphql_java_generator.client.request.AbstractGraphQLRequest.setStaticConfiguration(GraphQLConfiguration)
method, directly, or throughGraphQLRequest
that you've got from the query, mutation or subscription class (see the Execute GraphQL Requests page for more info on getting aGraphQLRequest
.- As this method is static, this configuration is then available for all your
GraphQLRequest
s. This is nice if you attack only one GraphQL server.
- As this method is static, this configuration is then available for all your
- Call the
setInstanceConfiguration(GraphQLConfiguration)
method of yourGraphQLRequest
that you've got from the query, mutation or subscription class (see the Execute GraphQL Requests page for more info on getting aGraphQLRequest
.- This allows to define various configuration, depending on your
GraphQLRequest
s, including calling several GraphQL servers.
- This allows to define various configuration, depending on your
- Create your query, mutation or subscription executor instance, from the generated code. For instance, if your GraphQL schema defines a QueryType, you can create an instance of it like this:
QueryTypeExecutor executor = new QueryTypeExecutor("https://your.server.com/your/graphql/path");
You'll find all the info on the OAuth2 client page
The generated code is created from Velocity templates. You can override any of these templates, according to your needs.
You'll find the needed info on the Custom Templates page.
GraphQL Variables are defined in the GraphQL spec:
- They are a GraphQL standard
- They respect strict syntactic rules
- They can be used only in Full Request, as they must be declared before used (with the exact GraphQL type)
- They can map a full field argument. For instance:
"mutation creation($human:HumanInput!){createHuman(human:$human){...}}
- They can map just part of a field argument. For instance:
"mutation creation($name:String!){createHuman(human:{name:$name, ...}){...}}
- Technically :
- The GraphQL request is sent as is (with the '$human' or '$name' in it, in the previous samples).
- The Graphql variable value is sent in the json
variables
field - The variable content is resolved on server side
Bind parameters are proper to this plugin:
- They are not a GraphQL standard
- They are easier to use (you don't have to declare them first)
- They can be used in both Full Requests and Partial Requests, as you don't have to declare them first
- They can be mandatory (if starting with '&') or optional (if starting with '?')
- If no value is provided at request execution time for a mandatory bind parameter, a
GraphQLRequestExecutionException
exception is thrown - If no value is provided at request execution time for an optional bind parameter, then this parameter is not sent to the GraphQL server
- If no value is provided at request execution time for a mandatory bind parameter, a
- They must map a full field argument.
- For instance, this full query is valid
"mutation {createHuman(human:&human){...}}
- For instance, this full query is not valid
"mutation {createHuman(human:{name:&name, ...}){...}}
- For instance, this full query is valid
- Technically :
- The bind parameters are replaced on client side by their value, before the GraphQL request is sent to the server
- A bind parameter value may not contain GraphQL variable in its field values.
This field is an optional field, described in the GraphQL spec. It contains a Map, and the values for this map is free, and may be anything, as choosed by the GraphQL server implementation.
To retrieve its value, you can do, for instance:
@Component
class AClass {
@Autowired
MyQueryTypeExecutor myQuery;
public void doSomething() {
// Retrieve the result as a full query
MyQueryType resp = myQuery.exec("{directiveOnQuery}");
// You can then retrieve the whole extensions field as a map
Map<String, JsonNode> map = resp.getExtensionsAsMap();
// Or retrieve just a value, from a key. This uses Jackson to deserialize
// the jsonNode into the target class for this key
YouClass value = resp.getExtensionsField("YourKey", YourClass.class);
... Do something useful
}
}
For Full requests, the request is the same as in graphiql:
@Component
public class YourClass {
@Autowired
QueryTypeExecutor queryExecutor;
GraphQLRequest request;
@PostConstruct
public void init() {
//From the Forum sample: nbBoards is a query that returns an Int, and has no arguments
request = queryExecutor.getTopicsGraphQLRequest("query {nbBoards}");
}
int nbBoards() {
return request.execQuery().getNbBoards();
}
}
For Partial Requests, just give an empty response field list:
@Component
public class YourClass {
@Autowired
QueryTypeExecutor queryExecutor;
GraphQLRequest request;
@PostConstruct
public void init() {
//From the Forum sample: nbBoards is a query that returns an Int, and has no arguments
request = queryExecutor.getNbBoardsGraphQLRequest("");
}
int nbBoards() {
return queryExecutor.nbBoards(request);
}
}
If you provide a full string, that contains all the parameters, you can do this:
public class MyClass {
@Autowired
AnotherMutationTypeExecutor mutationType;
public void myMethod() {
GraphQLRequest graphQLRequest = new GraphQLRequest(//
"mutation {createHuman (human: {name: \\\"a name with a string that contains a \\\\\\\", two { { and a } \\\", friends: [], appearsIn: [JEDI,NEWHOPE]} )"
+ "@testDirective(value:?value, anotherValue:?anotherValue, "
+ "anArray : [ \\\"a string that contains [ [ and ] that should be ignored\\\" , \\\"another string\\\" ] , \r\n"
+ "anObject:{ name: \\\"a name\\\" , appearsIn:[],friends : [{name:\\\"subname\\\",appearsIn:[],type:\\\"\\\"}],type:\\\"type\\\"}) "//
+ "{id name appearsIn friends {id name}}}"//
);
// You can can execute the full query, without providing any parameter (as everything is set in the provided request
Human human = mutationType.execWithBindValues(graphQLRequest, null).getCreateHuman();
}
}
Here are the main reasons (and solutions) that could be the source of this trouble:
- If you changed the plugin version, you have to force the full regeneration of the generated code. So don't forget to do a
maven clean install
or agradlew clean build
. - Perhaps you changed the plugin's version, but not the plugin runtime's version? Double check that their version are
- If you used custom templates, check if there was changes in the original custom templates. We may great efforts to let them as stable as possible. But if they changed, you may have to report theses changes into your custom templates.
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