diff --git a/spring-graphql-docs/modules/ROOT/pages/controllers.adoc b/spring-graphql-docs/modules/ROOT/pages/controllers.adoc index 2e0b384c..9090cda8 100644 --- a/spring-graphql-docs/modules/ROOT/pages/controllers.adoc +++ b/spring-graphql-docs/modules/ROOT/pages/controllers.adoc @@ -273,7 +273,7 @@ You can write a controller like this: [source,java,indent=0,subs="verbatim,quotes"] ---- @Controller - public class BookController { + public class ActivityController { @QueryMapping public List activities() { @@ -293,7 +293,7 @@ If necessary, you can take over the mapping for individual subtypes: [source,java,indent=0,subs="verbatim,quotes"] ---- @Controller - public class BookController { + public class ActivityController { @QueryMapping public List activities() { @@ -658,6 +658,17 @@ https://github.com/spring-projects/spring-graphql/issues/344#issuecomment-108281 for links to relevant issues and a suggested workaround. ==== +[[controllers.schema-mapping.localcontext]] +=== Local Context + +The main `GraphQlContext` is global for the entire query and can be used to store and retrieve cross-cutting context data for observability, security and more. +There are times when you would like to pass additional information to child fields data fetchers and avoid polluting the main context. +For such use cases, you should consider a local `GraphQLContext` as it is contained to a subset of the data fetching operations. +A well-known use case is https://www.graphql-java.com/blog/deep-dive-data-fetcher-results[data pre-fetching]. + +Controller methods can contribute a local context by returning a `DataFetcherResult` that holds the resolved data and the new context: + +include-code::LocalContextBookController[tag=localcontext,indent=0] [[controllers.batch-mapping]] diff --git a/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/controllers/schemamapping/localcontext/LocalContextBookController.java b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/controllers/schemamapping/localcontext/LocalContextBookController.java new file mode 100644 index 00000000..5371563f --- /dev/null +++ b/spring-graphql-docs/src/main/java/org/springframework/graphql/docs/controllers/schemamapping/localcontext/LocalContextBookController.java @@ -0,0 +1,79 @@ +/* + * Copyright 2020-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.docs.controllers.schemamapping.localcontext; + +import java.util.ArrayList; +import java.util.List; + +import graphql.GraphQLContext; +import graphql.execution.DataFetcherResult; + +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.LocalContextValue; +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.graphql.data.method.annotation.SchemaMapping; +import org.springframework.stereotype.Controller; + +// tag::localcontext[] +@Controller +public class LocalContextBookController { + + @QueryMapping + public DataFetcherResult bookById(@Argument Long id) { + // Our controller method must return a DataFetcherResult + DataFetcherResult.Builder resultBuilder = DataFetcherResult.newResult(); + BookAndAuthor bookAndAuthor = this.fetchBookAndAuthorById(id); + + // Create a new local context and store the author value + GraphQLContext localContext = GraphQLContext.getDefault() + .put("author", bookAndAuthor.author); + return resultBuilder + .data(bookAndAuthor.book) + .localContext(localContext) + .build(); + } + + @SchemaMapping + public List related(Book book, @LocalContextValue Author author) { + List relatedBooks = new ArrayList<>(); + relatedBooks.addAll(fetchBooksByAuthor(author)); + relatedBooks.addAll(fetchSimilarBooks(book)); + return relatedBooks; + } + + // end::localcontext[] + + private BookAndAuthor fetchBookAndAuthorById(Long id) { + return new BookAndAuthor(new Book(id, "Spring for GraphQL", 12L), + new Author(1L, "Jane Doe")); + } + + private List fetchBooksByAuthor(Author author) { + return List.of(new Book(1, "Spring for GraphQL", 12L)); + } + + private List fetchSimilarBooks(Book book) { + return List.of(); + } + + record BookAndAuthor(Book book, Author author) {} + + record Book(long id, String title, long authorId) {} + + record Author(long id, String name) {} + +}