-
Notifications
You must be signed in to change notification settings - Fork 49
server_faq
- Questions about the server configuration
-
Personalize your GraphQL server
- Change the generated code (custom templates)
- Override the type wiring and/or the DataFetcher definitions
- Override the DataFetchers or Spring Controllers (= change the way the data is fetched for one field of one object)
- HowTo provide your GraphQL ExecutionInputCustomizer, ExecutionResultHandler, GraphQLInvocation, GraphQLController and JacksonJsonSerializer
- Errors during execution
The server is a Spring Boot app or servlet. It is configured through one of the application.properties
or the application.yml
files. It should be available in the root of the classpath, so you should provide is in the project's src/main/resources folder.
The below sample is based on the allGraphQLCases-server, available as a sample in the Maven and Gradle projects:
# Changing the port for the GraphQL server
server:
port: 8180
# Changing the server path
graphql:
url: /my/updated/graphql/path
Everything is explained in the server OAuth2 page
You can find a sample, with the OAuth2 configuration, that includes configuring Spring Security beans.
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.
A key point of the GraphQL server behavior is the link between the GraphQL schema and the server code. This link is defined in the type wiring, and this type wiring is coded in the generated GraphQLWiring
class.
In this class, you'll find the buildWiring()
method, that defines the wiring for each type, custom scalar, interface and union defined in the GraphQL schema. The wiring for each is defined in a dedicated method, as you can see in the below extract of this generated code:
@Component
public class GraphQLWiring {
@Autowired
protected GraphQLDataFetchers graphQLDataFetchers;
protected RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
//
// Wiring every custom scalar definitions
.scalar(com.graphql_java_generator.customscalars.GraphQLScalarTypeDate.Date)
...
// Wiring every GraphQL type
.type("MyQueryType", typeWiring -> addWiringForMyQueryType(typeWiring))
.type("AnotherMutationType", typeWiring -> addWiringForAnotherMutationType(typeWiring))
.type("TheSubscriptionType", typeWiring -> addWiringForTheSubscriptionType(typeWiring))
.type("AllFieldCases", typeWiring -> addWiringForAllFieldCases(typeWiring))
...
.build();
} // buildWiring()
// Declares a DataFetcher for each
protected TypeRuntimeWiring.Builder addWiringForMyQueryType(TypeRuntimeWiring.Builder typeWiring) {
typeWiring.dataFetcher("withoutParameters", graphQLDataFetchers.dataFetchersDelegateMyQueryTypeWithoutParameters());
typeWiring.dataFetcher("withOneOptionalParam", graphQLDataFetchers.dataFetchersDelegateMyQueryTypeWithOneOptionalParam());
...
return typeWiring;
}
...
}
Since version 1.12.4, you can override:
- The whole wiring (by overriding the
buildWiring()
) method of theGraphQLWiring
class - The wiring for one type, by overriding one of the
addWiringForXxxx(TypeRuntimeWiring.Builder)
method of theGraphQLWiring
class (where Xxx is the name for the GraphQL object) - The dataFetcher for one field of one object, by overriding just one method `GraphQLDataFetchers``class (see below)
For points 1 and 2, see just below. For point 3, see in the paragraph below (Override the DataFetchers)
To override the GraphQLWiring
class, you'll just have to define a Spring Component, that inherits from the GraphQLWiring
class. To do this:
- Create a class in the package where the GraphQL classes is declared, or one of its subpackages, so that Spring can find it
- It's the package you declare in the packageName plugin parameter of your pom or build.gradle file. If you didn't declare this, the package is the default one, that is: com.generated.graphql
- Give it another name, like
CustomGraphQLWiring
- Make it inherit from the
GraphQLWiring
class. This is mandatory, so that it is recognized and used. This also allows you to benefit from the generated code. You may then override only part of the wiring. - Mark it as Spring bean, with the
@Component
Spring annotation - Mark it with the
@Primary
annotation, so that it overrides the one defined in the generated code
Here is a sample, extracted from the provided Forum sample. This sample overrides the GraphQLWiring
class, with the CustomGraphQLWiring.
Here is the content of this class:
@Component
@Primary
public class CustomGraphQLWiring extends GraphQLWiring {
@Override
protected TypeRuntimeWiring.Builder addWiringForSubscription(TypeRuntimeWiring.Builder typeWiring) {
typeWiring.dataFetcher("subscribeToNewPost",
graphQLDataFetchers.dataFetchersDelegateSubscriptionSubscribeToNewPost());
return typeWiring;
}
}
Override the DataFetchers or Spring Controllers (= change the way the data is fetched for one field of one object)
As defined on this page, graphql-java uses data fetchers (also named resolvers) to fetch data. There is one data fetcher:
- For each field of your query, mutation and subscription classes
- For each field of your GraphQL type or interface that is not a scalar.
As specified in the server page, it's up to you to implement these data fetchers. So the generated code generates interfaces named DataFetcherDelegate
, that define the expected method for each data fetcher, that you should implement. This should work for most GraphQL server implementations.
But it may happen that you're not happy with these methods.
So, since version 1.12.4, all the data fetchers for your server are defined in the generated GraphQLDataFetchers
class. You can override this class, and provide an alternate data fetcher for any data fetcher.
To override the GraphQLDataFetchers
class, you'll just have to define a Spring Component, that inherits from the GraphQLWiring
class. To do this:
- Create a class in the package where the GraphQL classes is declared, or one of its subpackages, so that Spring can find it
- It's the package you declare in the packageName plugin parameter of your pom or build.gradle file. If you didn't declare this, the package is the default one, that is: com.generated.graphql
- Give it another name, like CustomGraphQLDataFetchers
- Make it inherit from the
GraphQLDataFetchers
class. This is mandatory, so that it is recognized and used. This also allows you to benefit from the generated code. You may then override only part of the wiring. - Mark it as Spring bean, with the
@Component
Spring annotation - Mark it with the
@Primary
annotation, so that it overrides the one defined in the generated code
Here is a sample, extracted from the provided Forum sample. This sample overrides the GraphQLDataFetchers
class, with the CustomGraphQLDataFetchers.
Here is the content of this class:
@Component("overridenGraphQLDataFetchers")
@Primary
public class CustomGraphQLDataFetchers extends org.forum.server.graphql.GraphQLDataFetchers {
@Override
public DataFetcher<Board> dataFetchersDelegateMutationCreateBoard() {
return dataFetchingEnvironment -> {
String name = (String) graphqlUtils.getArgument(dataFetchingEnvironment.getArgument("name"),
"${argument.type.graphQLTypeSimpleName}", "java.lang.Long", String.class);
Boolean publiclyAvailable = (Boolean) graphqlUtils.getArgument(
dataFetchingEnvironment.getArgument("publiclyAvailable"), "${argument.type.graphQLTypeSimpleName}",
"java.lang.Long", Boolean.class);
Board ret = null;
try {
// HERE IS WHAT's CHANGED IN THIS OVERRIDEN VERSION:
// We add " (Overridden DataFetcher)" to the name
ret = dataFetchersDelegateMutation.createBoard(dataFetchingEnvironment,
name + " (Overridden DataFetcher)", publiclyAvailable);
} catch (NoSuchElementException e) {
// There was no items in the Optional
}
if (ret != null)
logger.debug("createBoard (Overridden DataFetcher): 1 result found");
else
logger.debug("createBoard (Overridden DataFetcher): no result found");
return ret;
};
}
}
In version 2.x, it's possible to override the default GraphQL Spring Mappings.
The [ignoredSpringMappings](https://graphql-maven-plugin-project.graphql-java-generator.com/graphql-maven-plugin/plugin-info.html)
plugin parameters allow to declare a list of GraphQL mappings that will be ignored by the plugin. These ignored mappings can then be defined by the specific implementation.
The other way to it is to create a spring GraphQL Controller, that overrides the controller generated by the plugin. But this may lead to this error: Ambiguous mapping. Cannot map 'xxxController' method [...] to 'Type.field': there is already 'yyy' bean method [...] mapped
.
The parameter contains a list of:
- GraphQL type name: The full controller class for this type is ignored, and won't be generated
- GraphQL type's field name: The method in the controller of this type, for this field, is ignored, and won't be generated. The field must be written like this:
{type name}.{field name}
The accepted separators for the values are: comma, space, carriage return, end of line, space, tabulation. At least one separator must exist between two values in the list. Here is a sample:
<ignoredSpringMappings>Type1, Type2.field1
Type3
Type4.field2
</ignoredSpringMappings>
For field mapping, there must be no separator other than '.' between the type name and the field name. For instance, the following type declaration are invalid: 'type .field', 'type. field'
HowTo provide your GraphQL ExecutionInputCustomizer, ExecutionResultHandler, GraphQLInvocation, GraphQLController and JacksonJsonSerializer
The plugin uses a clone of the graphql-java-spring project. We provided PR for this project, but as it seems to not be maintained, we cloned it into the plugin project, until the PR are accepted and a new version is published.
As defined in the graphql-java-spring project, you can override the main GraphQL component, that is:
-
DefaultExecutionInputCustomizer implements the
customizeExecutionInput(ExecutionInput, WebRequest)
method -
DefaultExecutionResultHandler implements the
handleExecutionResult(CompletableFuture<ExecutionResult>)
method -
DefaultGraphQLInvocation implements the
invoke(GraphQLInvocationData, WebRequest)
method. -
GraphQLController is the Spring Controller that receives the GET and POST requests, that is: the queries and mutations.
- Note: the subscriptions are managed by the generated
WebSocketHandler
- Note: the subscriptions are managed by the generated
- JacksonJsonSerializer manages the json serialization and deserialization
To replace any of these implementations, you just have to provide a Spring Bean that implements the same interface, like this:
- Create a class in the package where the GraphQL classes is declared, or one of its subpackages, so that Spring can find it
- It's the package you declare in the packageName plugin parameter of your pom or build.gradle file. If you didn't declare this, the package is the default one, that is: com.generated.graphql
- Give it another name, like CustomXxxx, where Xxx is the name of the interface you implement
- Make it implement the interface of the component you want to override (or inherit from it)
- Mark it as Spring bean, with the
@Component
Spring annotation - Mark it with the
@Primary
annotation, so that it overrides the one defined in the generated code
It means that the server doesn't properly manage web sockets.
Please read the Subscription page.
For 2.x versions, please read the Server migration from 1.x to 2.x page, especially the Support of WebSocket (mandatory for subscriptions) part.
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