From 50eef472c5800fd1f5ad170b8fd34791b317dc02 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 11 Aug 2021 05:23:17 +0100 Subject: [PATCH] Add reference docs for annotated DataFetcher's Closes gh-90 --- .../src/docs/asciidoc/boot-starter.adoc | 33 +-- .../src/docs/asciidoc/index.adoc | 236 +++++++++++++++++- 2 files changed, 243 insertions(+), 26 deletions(-) diff --git a/spring-graphql-docs/src/docs/asciidoc/boot-starter.adoc b/spring-graphql-docs/src/docs/asciidoc/boot-starter.adoc index b94c98d6..3893e40b 100644 --- a/spring-graphql-docs/src/docs/asciidoc/boot-starter.adoc +++ b/spring-graphql-docs/src/docs/asciidoc/boot-starter.adoc @@ -113,30 +113,19 @@ spring.graphql.schema.printer.enabled=false ---- -[[boot-graphql-datafetcher]] -== `DataFetcher` Registration +[[boot-graphql-runtimewiring]] +== RuntimeWiring -You can declare `RuntimeWiringConfigurer` beans in your Spring config and use those to -register data fetchers, type resolvers, and more with the GraphQL engine: +The GraphQL Java `RuntimeWiring.Builder` can be used to register ``DataFetcher``s, +type resolvers, custom scalar types, and more. You can declare `RuntimeWiringConfigurer` +beans in your Spring config to get access to the `RuntimeWiring.Builder`. The Boot +starter detects such beans adds them to <>. -[source,java,indent=0,subs="verbatim,quotes"] ----- -@Component -public class PersonDataWiring implements RuntimeWiringConfigurer { - - private final PersonService service; - - public PersonDataWiring(PersonService service) { - this.service = service; - } - - @Override - public void configure(RuntimeWiring.Builder builder) { - builder.type("Query", wiring -> - wiring.dataFetcher("people", env -> this.service.findAll())); - } -} ----- +Typically, however, applications will not implement ``DataFetcher`` directly and will +instead create <>. The Boot +starter declares a `RuntimeWiringConfigurer` called `AnnotatedDataFetcherConfigurer` that +detects `@GraphQlController` classes with annotated handler methods and registers those +as ``DataFetcher``s. [[boot-repositories-querydsl]] diff --git a/spring-graphql-docs/src/docs/asciidoc/index.adoc b/spring-graphql-docs/src/docs/asciidoc/index.adoc index 20bf7701..925680b6 100644 --- a/spring-graphql-docs/src/docs/asciidoc/index.adoc +++ b/spring-graphql-docs/src/docs/asciidoc/index.adoc @@ -188,8 +188,8 @@ support for <>, <>, and === Reactive `DataFetcher` The default `GraphQlSource` builder enables support for a `DataFetcher` to return `Mono` -or `Flux`. Both return types are adapted to a `CompletableFuture` with `Flux` values -aggregated and turned into a List, unless the request is a GraphQL subscription request, +or `Flux` which adapts those to a `CompletableFuture` where `Flux` values are aggregated +and turned into a List, unless the request is a GraphQL subscription request, in which case the return value remains a Reactive Streams `Publisher` for streaming GraphQL responses. @@ -316,11 +316,11 @@ Then use it to create a `DataFetcher`: [source,java,indent=0,subs="verbatim,quotes"] ---- - // For single result queries + // For single result queries DataFetcher dataFetcher = QuerydslDataFetcher.builder(repository).single(); - // For multi-result queries + // For multi-result queries DataFetcher> dataFetcher = QuerydslDataFetcher.builder(repository).many(); ---- @@ -367,6 +367,234 @@ Such repositories are auto-detected in the < + public String hello() { // <2> + return "Hello, world!"; + } + + } +---- +<1> Bind this method to a query, i.e. a field under the Query type. +<2> Determine the query from the method name if not declared on the annotation. + +Spring GraphQL uses `RuntimeWiring.Builder` to register the above handler method as a +`graphql.schema.DataFetcher` for the query named "hello". + + +[[controllers-declaration]] +=== Declaration + +You can define `@GraphQlController` beans as standard Spring bean definitions. The +`@GraphQlController` stereotype allows for auto-detection, aligned with Spring general +support for detecting `@Controller` and `@Component` classes on the classpath and +auto-registering bean definitions for them. It also acts as a stereotype for the annotated +class, indicating its role as a data fetching component in a GraphQL application. + +`AnnotatedDataFetcherConfigurer` detects `@GraphQlController` beans and registers their +annotated handler methods as ``DataFetcher``s via `RuntimeWiring.Builder`. It is an +implementation of `RuntimeWiringConfigurer` which can be added to `GraphQlSource.Builder`. +The Spring Boot starter automatically declares `AnnotatedDataFetcherConfigurer` as a bean +and adds all `RuntimeWiringConfigurer` beans to `GraphQlSource.Builder` and that enables +support for annotated ``DataFetcher``s, see <>. + + +[[controllers-mapping]] +=== Mapping + +The `@SchemaMapping` annotation maps a handler method to a field in the GraphQL schema +and declares it to be the `DataFetcher` for that field. The annotation can specify the +parent type name, and the field name: + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @GraphQlController + public class BookController { + + @SchemaMapping(typeName="Book", field="author") + public Author getAuthor(Book book) { + // ... + } + } +---- + +The `@SchemaMapping` annotation can also leave out those attributes, in which case the +field name defaults to the method name, while the type name defaults to the simple class +name of the source/parent object injected into the method. For example, the below +defaults to type "Book" and field "author": + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @GraphQlController + public class BookController { + + @SchemaMapping + public Author author(Book book) { + // ... + } + } +---- + +The `@SchemaMapping` annotation can be declared at the class level to specify a default +type name for all handler methods in the class. + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @GraphQlController + @SchemaMapping(typeName="Book") + public class BookController { + + // @SchemaMapping methods for fields of the "Book" type + + } +---- + +`@QueryMapping`, `@MutationMapping`, and `@SubscriptionMapping` are meta annotations that +are themselves annotated with `@SchemaMapping` and have the typeName preset to `Query`, +`Mutation`, or `Subscription` respectively. Effectively, these are shortcut annotations +for fields under the Query, Mutation, and Subscription types respectively. For example: + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @GraphQlController + public class BookController { + + @QueryMapping + public Book bookById(@Argument Long id) { + // ... + } + + @MutationMapping + public Book addBook(@Argument BookInput bookInput) { + // ... + } + + @SubscriptionMapping + public Flux newPublications() { + // ... + } + } +---- + + +[[controllers-methods]] +=== Handler Methods + +`@SchemaMapping` handler methods have flexible signatures and can choose from a range of +method arguments and return values.. + + +[[controllers-arguments]] +==== Method Arguments + +Annotated handler methods can choose from one of the following method arguments: + +[cols="1,2"] +|=== +| Method Argument | Description + +| `@Argument` +| For access to field arguments with conversion. + See <>. + +| Source +| For access to the source (i.e. parent/container) instance of the field. + See <>. + +| `DataFetchingEnvironment` +| For direct access to the underlying `DataFetchingEnvironment`. + See <>. + +|=== + + +[[controllers-return-values]] +==== Return Values + +Annotated handler methods can return any value, including Reactor `Mono` and `Flux` as +described in <>. + + + +[[controllers-argument]] +==== `@Argument` + +In GraphQL Java, the `DataFetchingEnvironment` provides access to field-specific argument +values. The arguments are available as simple scalar values such as String, or as a `Map` +of values for more complex input, or a `List` of values. + +Use `@Argument` to access an argument for the field that maps to the handler method. You +can declare such a method parameter to be of any type. If necessary, Spring GraphQL +converts the value by serializing it to JSON first and then to the target type. + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @GraphQlController + public class BookController { + + @QueryMapping + public Book bookById(@Argument Long id) { + // ... + } + + @MutationMapping + public Book addBook(@Argument BookInput bookInput) { + // ... + } + } +---- + +You can explicitly specify the argument name, for example `@Argument("bookInput")`, or if +it not specified, it defaults to the method parameter name, but this requires the +`-parameters` compiler flag with Java 8+ or debugging information from the compiler. + +By default, an `@Argument` is required, but you can make it optional by setting the +`required` flag to false or by declaring the argument with `java.util.Optional`. + + +[[controllers-source]] +==== Source + +In GraphQL Java, the `DataFetchingEnvironment` provides access to the source (i.e. +parent/container) instance of the field. To access this, simply declare a method parameter +of the expected target type. + +[source,java,indent=0,subs="verbatim,quotes"] +---- + @GraphQlController + public class BookController { + + @SchemaMapping + public Author author(Book book) { + // ... + } + } +---- + +The source method argument also helps to determine the type name for the mapping. +If the simple name of the Java class matches the GraphQL type, then there is no need to +explicitly specify the type name in the `@SchemaMapping` annotation. + + +[[controllers-environment]] +==== `DataFetchingEnvironment` + +To access the `DataFetchingEnvironment` directly, simply declare a method parameter of +the same type. + + + [[security]] == Security