How to use variables in graphql query via dgs java graphql client? #1944
-
Hi all. This is a quote from a post on SO, maybe someone here can help. I am using dgs java graphql client in my code. I want to build a query and specify variables in the query, I want to get something like this:: {
"query": "query ExampleQuery($first: Int, $filter: ExampleFilter) { exampleData(first: $first, filter: $filter) { id }",
"variables": {
"filter": {
"field": "test"
},
"first": 25
}
} To do this I write something like the following code: GraphQLQueryRequest request = new GraphQLQueryRequest(
ExampleDataGraphQLQuery.newRequest()
.queryName("ExampleQuery")
.filter(ExampleFilterInput.newBuilder().field("test").build())
.first(25),
new ExampleProjectionRoot<>().id()
);
Example response = graphQLClient
.executeQuery(request.serialize(), request.getQuery().getInput())
.extractValueAsObject(DgsConstants.QUERY.Example, Example.class); But in the end, when executing a request, I get that it is not the passed variables that are used, but static values directly in the request, something like this: {
"query": "exampleData(first: 25, filter: { field: \"test\"}) { id }",
"variables": {
"filter": {
"field": "test"
},
"first": 25
}
} That is, the variables are not actually used, although they are present in the request body. I realize that I haven't associated variables with the request body myself, but I haven't found a clear way to substitute variables into a request either in the documentation or in other examples elsewhere. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
Your question makes sense, but that's actually not supported with the codegen queries. The thinking is that if you're constructing your query in code like that, you can just pass in the variables there in the Java code. I agree that it's a bit confusing that the codegen query API doesn't support variables, while the If you, for some reason, need to use variables, I would recommend using a multiline String in Java/Kotlin and write the query that way. With Intellij you also have the `@Language("GraphQL") annotation which makes that a pretty good option. |
Beta Was this translation helpful? Give feedback.
-
I wanted to do this and found an (evil) way to achieve it. Firstly I change the javac settings so that it passes Then I wrote some horrid code to spider over the graphql query and replace the inline inputs with variable references and move the inputs into variables. You can then submit the query with the returned variables. public static Map<String, Object> replaceInputsWithVariableDeclaration(final GraphQLQuery query) {
final var inputs = query.getInput();
final var variables = Map.copyOf(inputs);
final List<VariableDefinition> variableDefinitions = createVariableDefinitions(query);
variableDefinitions.forEach(definition -> inputs.put(definition.getName(), new VariableReference(definition.getName())));
query.getVariableDefinitions().addAll(variableDefinitions);
return variables;
}
private static List<VariableDefinition> createVariableDefinitions(final GraphQLQuery query) {
// Use the constructor with arguments to figure out what graphql types the inputs have.
// NB This only works because the compiler has the `-parameters` flag set.
Constructor<?> constructor = stream(query.getClass().getDeclaredConstructors()).filter(candidate -> candidate.getParameters().length > 0).findFirst().orElseThrow();
var parametersByName = stream(constructor.getParameters()).collect(Collectors.toMap(Parameter::getName, Function.identity()));
class VariableDefinitionFactory {
Parameter get(final String name) {
return Optional.ofNullable(parametersByName.get(name)).orElseThrow(
() -> new IllegalStateException("No parameter found for name " + name + " in paramters " + parametersByName.keySet())
);
}
VariableDefinition create(final String name) {
return createVariableDefinition(get(name));
}
private VariableDefinition createVariableDefinition(final Parameter parameter) {
return new VariableDefinition(parameter.getName(), convertType(parameter.getParameterizedType()));
}
}
VariableDefinitionFactory factory = new VariableDefinitionFactory();
return query.getInput().keySet().stream().map(factory::create).collect(Collectors.toList());
}
/**
* Converts the java type to the graphql type.
* Sadly this can't be inferred from the DGS codegen apart from via some yucky reflection.
*/
private static Type<?> convertType(final java.lang.reflect.Type javaType) {
if (javaType instanceof Class<?> type) {
return new NonNullType(new TypeName(convertTypeName(type)));
}
if (javaType instanceof ParameterizedType parameterizedType) {
final var rawType = parameterizedType.getRawType();
if (rawType.equals(List.class)) {
// If a list then recurse.
final var listType = parameterizedType.getActualTypeArguments()[0];
return new NonNullType(new ListType(convertType(listType)));
}
}
throw new AssertionError("Unknown type " + javaType);
}
/**
* For the generated classes and most scalars the simple name of the class will match the graphql type.
*/
@NotNull
private static String convertTypeName(final Class<?> type) {
// `int.class` will do the right thing but not `Integer`.
if (type.equals(Integer.class)) {
return "Int";
}
String simpleName = type.getSimpleName();
if (type.isPrimitive()) {
// e.g. boolean.class => Boolean
return simpleName.substring(0, 1).toUpperCase(Locale.ROOT) + simpleName.substring(1);
}
return simpleName;
} |
Beta Was this translation helpful? Give feedback.
Your question makes sense, but that's actually not supported with the codegen queries. The thinking is that if you're constructing your query in code like that, you can just pass in the variables there in the Java code.
The practical problem would be that the arguments in the codegen query are typed, so an
Int
is anInteger
in Java, which also means you wouldn't be able to pass in a$myVar
there.I agree that it's a bit confusing that the codegen query API doesn't support variables, while the
queryExecutor
clearly does, but technically these APIs are completely separated.If you, for some reason, need to use variables, I would recommend using a multiline String in Java/Kotlin and write th…