In a Spring Boot application, we're already having a fully functional GraphQL endpoint, serving .graphqls
files via GraphQL Java Tools (we included the graphql-spring-boot-starter
dependency) and handling the data resolution through our base Query
class implementing GraphQLQueryResolver
and subsequent GraphQLResolver
's.
For a business need, we have to re-create standard REST API endpoints, so I wonder why not just making calls to GraphQL (instead of having to re-implement "by hand" the data resolution once again)?
And as it's in the same backend application, no need to make HTTP or servlet (ForwardRequest) calls, just call some API's in Java.
The thing is I don't know how to proceed.
I read this example, but it's with basic GraphQL Java (not Tools):
https://www.graphql-java.com/documentation/v9/execution/
I know this should possible because we are allowed to do this in tests:
https://github.com/graphql-java-kickstart/graphql-spring-boot/blob/master/example-graphql-tools/src/test/java/com/graphql/sample/boot/GraphQLToolsSampleApplicationTest.java
But how to do it in regular code? There is not such thing as a GraphQLTemplate
.
I also tried to search through examples at:
- https://github.com/graphql-java-kickstart/graphql-java-tools/tree/master/example
- https://github.com/graphql-java-kickstart/graphql-spring-boot
but found nothing relevant to our need.
Found nothing more in Documentation:
- https://www.graphql-java-kickstart.com/tools/
- https://www.graphql-java-kickstart.com/spring-boot/
What did I miss? Ideally I'm looking to inject some GraphQLSomething
like this:
@RestController
@RequestMapping(path = "api")
public class CompanyController {
@Autowired
private GraphQLSomething graphQLSomething;
@GetMapping("company/{id}")
public ResponseEntity<?> societe(@PathVariable @NotNull Integer id) {
GraphQLSomethingResult result = GraphQLSomething.query("company(id: $id) { id name andsoone }", "{ \"id\": 123456 }").execute(); // not really sure of the GraphQL syntax here, but it'll need some tests...
return result.getDataOrElse();
}
}
Finally found how to do the thing as I wanted:
import java.util.Map;
import java.util.Optional;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.collect.ImmutableMap;
import graphql.ExecutionResult;
import graphql.servlet.core.GraphQLQueryInvoker;
import graphql.servlet.core.internal.GraphQLRequest;
import graphql.servlet.input.GraphQLInvocationInputFactory;
import graphql.servlet.input.GraphQLSingleInvocationInput;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Validated
@RestController
@RequestMapping(path = "api")
public class CompanyController {
@Autowired
private GraphQLInvocationInputFactory invocationInputFactory;
@Autowired
private GraphQLQueryInvoker queryInvoker;
@GetMapping("company/{id}")
public ResponseEntity<?> societe(@PathVariable @NotNull Integer id) {
String query = "query ($id: Int!) { company(id: $id) { id name andsoon } }";
/*
* ImmutableMap is a Guava class; you can build the map (e.g. a HashMap) on your
* own, or simply Map.to(..) in Java 9, or even @PathVariable Map<String,
* Object> variables as the method's parameter instead (but you'll miss the
* validation).
*/
Map<String, Object> variables = ImmutableMap.of("id", id);
GraphQLRequest request = new GraphQLRequest(query, variables, null);
GraphQLSingleInvocationInput invocationInput = invocationInputFactory.create(request);
ExecutionResult result = queryInvoker.query(invocationInput);
/*
* Of course result.getData() can be null here - see also result.isDataPresent()
* - but data/error handling's left to you
*/
Optional<Object> company = Optional.ofNullable(result.getData().get("company"));
return ResponseEntity.of(company);
}
}
FYI, to get the dependencies for the above code, you'll need this:
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-autoconfigure</artifactId>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-servlet</artifactId>
<version>6.1.3</version>
</dependency>