GraphQL Servlet
Implementation of GraphQL Java Servlet including support for Relay.js, Apollo and OSGi out of the box. This project wraps the Java implementation of GraphQL provided by GraphQL Java. See GraphQL Java documentation for more in depth details regarding GraphQL Java itself.
We try to stay up to date with GraphQL Java as much as possible. The current version supports GraphQL Java 11.0.
This project requires at least Java 8.
Quick start
See Getting started for more detailed instructions.
To add graphql-java-servlet
to your project and get started quickly, do the following.
Build with Gradle
Make sure mavenCentral
is amongst your repositories:
repositories {
mavenCentral()
}
Add the graphql-java-servlet
dependency:
dependencies {
compile 'com.graphql-java-kickstart:graphql-java-servlet:7.1.1'
}
Build with Maven
Add the graphql-java-servlet
dependency:
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-java-servlet</artifactId>
<version>7.1.1</version>
</dependency>
Create a Servlet class
Creating the Servlet class requires various parameters to be provided at the moment. We're working on simplifying this, to make it easier to get started. For now, take a look at Create a Servlet class to see what's needed to create a Servlet with a schema.
Using the latest development build
Snapshot versions of the current master
branch are availble on JFrog. Check the next snapshot version in
gradle.properties.
Build with Gradle
Add the Snapshot repository:
repositories {
mavenCentral()
maven { url "http://oss.jfrog.org/artifactory/oss-snapshot-local" }
}
Build with Maven
Add the Snapshot repository:
<repositories>
<repository>
<id>oss-snapshot-local</id>
<name>jfrog</name>
<url>http://oss.jfrog.org/artifactory/oss-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</repository>
</repositories>
Usage
The servlet supports the following request formats:
- GET request to
../schema.json
: Get the result of an introspection query. - GET request with query parameters (query only, no mutation):
- query
- operationName (optional)
- variables (optional)
- POST body JSON object with fields:
- query
- operationName (optional)
- variables (optional)
- POST multipart part named "graphql" containing JSON object with fields:
- query
- operationName (optional)
- variables (optional)
- POST multipart parts named "query", "operationName" (optional), and "variables" (optional)
Servlet Listeners
You can also add servlet listeners to an existing servlet. These listeners provide hooks into query execution (before, success, failure, and finally) and servlet execution (before, success, error, and finally):
servlet.addListener(new GraphQLServletListener() {
@Override
GraphQLServletListener.RequestCallback onRequest(HttpServletRequest request, HttpServletResponse response) {
return new GraphQLServletListener.RequestCallback() {
@Override
void onSuccess(HttpServletRequest request, HttpServletResponse response) {
}
@Override
void onError(HttpServletRequest request, HttpServletResponse response, Throwable throwable) {
}
@Override
void onFinally(HttpServletRequest request, HttpServletResponse response) {
}
}
}
@Override
GraphQLServletListener.OperationCallback onOperation(GraphQLContext context, String operationName, String query, Map<String, Object> variables) {
return new GraphQLServletListener.OperationCallback() {
@Override
void onSuccess(GraphQLContext context, String operationName, String query, Map<String, Object> variables, Object data) {
}
@Override
void onError(GraphQLContext context, String operationName, String query, Map<String, Object> variables, Object data, List<GraphQLError> errors) {
}
@Override
void onFinally(GraphQLContext context, String operationName, String query, Map<String, Object> variables, Object data) {
}
}
}
})
Relay.js support
Relay.js support is provided by the EnhancedExecutionStrategy of graphql-java-annotations. You MUST pass this execution strategy to the servlet for Relay.js support.
This is the default execution strategy for the OsgiGraphQLServlet
, and must be added as a dependency when using that servlet.
Apollo support
Query batching is supported, no configuration required.
Spring Framework support
To use the servlet with Spring Framework, either use the Spring Boot starter or simply define a ServletRegistrationBean
in a web app:
@Bean
ServletRegistrationBean graphQLServletRegistrationBean(GraphQLSchema schema, ExecutionStrategy executionStrategy, List<GraphQLOperationListener> operationListeners) {
return new ServletRegistrationBean(new SimpleGraphQLServlet(schema, executionStrategy, operationListeners), "/graphql");
}
OSGI support
The OsgiGraphQLServlet uses a "provider" model to supply the servlet with the required objects:
- GraphQLQueryProvider: Provides query fields to the GraphQL schema.
- GraphQLMutationProvider: Provides mutation fields to the GraphQL schema.
- GraphQLTypesProvider: Provides type information to the GraphQL schema.
- ExecutionStrategyProvider: Provides an execution strategy for running each query.
- GraphQLContextBuilder: Builds a context for running each query.
Examples
You can now find some example on how to use graphql-java-servlet.
OSGi Examples
Requirements
The OSGi examples use Maven as a build tool because it requires plugins that are not (yet) available for Gradle. Therefore you will need Maven 3.2+.
Building & running the OSGi examples
You can build the OSGi examples sub-projects by simply executing the following command from the examples/osgi directory:
mvn clean install
This will generate a complete Apache Karaf distribution in the following files:
examples/osgi/apache-karaf-package/target/graphql-java-servlet-osgi-examples-apache-karaf-package-VERSION.tar.gz(.zip)
You can simply uncompress this file and launch the OSGi server using the command from the uncompressed directory:
bin/karaf
You should then be able to access the GraphQL endpoint at the following URL once the server is started:
http://localhost:8181/graphql/schema.json
If you see the JSON result of an introspection query, then all is ok. If not, check the data/log/karaf.log file for any errors.
We also provide a script file to do all of the building and running at once (only for Linux / MacOS ):
./buildAndRun.sh
Deploying inside Apache Karaf server
You can use the graphql-java-servlet as part of an Apache Karaf feature, as you can see in the example project here:
And here is a sample src/main/feature/feature.xml file to add some dependencies on other features:
Example GraphQL provider implementation
Here's an example of a GraphQL provider that implements three interfaces at the same time.
Request-scoped DataLoaders
It is possible to use dataloaders in a request scope by customizing GraphQLContextBuilder. And instantiating a new DataLoaderRegistry for each GraphQLContext. For eg:
public class CustomGraphQLContextBuilder implements GraphQLContextBuilder {
private final DataLoader userDataLoader;
public CustomGraphQLContextBuilder(DataLoader userDataLoader) {
this.userDataLoader = userDataLoader;
}
@Override
public GraphQLContext build(HttpServletRequest req) {
GraphQLContext context = new GraphQLContext(req);
context.setDataLoaderRegistry(buildDataLoaderRegistry());
return context;
}
@Override
public GraphQLContext build() {
GraphQLContext context = new GraphQLContext();
context.setDataLoaderRegistry(buildDataLoaderRegistry());
return context;
}
@Override
public GraphQLContext build(HandshakeRequest request) {
GraphQLContext context = new GraphQLContext(request);
context.setDataLoaderRegistry(buildDataLoaderRegistry());
return context;
}
private DataLoaderRegistry buildDataLoaderRegistry() {
DataLoaderRegistry dataLoaderRegistry = new DataLoaderRegistry();
dataLoaderRegistry.register("userDataLoader", userDataLoader);
return dataLoaderRegistry;
}
}
It is then possible to access the DataLoader in the resolvers by accessing the [DataLoaderRegistry] from context. For eg:
public CompletableFuture<String> getEmailAddress(User user, DataFetchingEnvironment dfe) { // User is the graphQL type
final DataLoader<String, UserDetail> userDataloader =
dfe.getContext().getDataLoaderRegistry().get().getDataLoader("userDataLoader"); // UserDetail is the data that is loaded
return userDataloader.load(User.getName())
.thenApply(userDetail -> userDetail != null ? userDetail.getEmailAddress() : null);
}