Skip to content

Commit d836f68

Browse files
committed
Add documentation for federation support
Closes gh-864
1 parent 784b637 commit d836f68

File tree

4 files changed

+126
-2
lines changed

4 files changed

+126
-2
lines changed

spring-graphql-docs/modules/ROOT/nav.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* xref:security.adoc[]
77
* xref:observability.adoc[]
88
* xref:graalvm-native.adoc[]
9+
* xref:federation.adoc[]
910
* xref:client.adoc[]
1011
* xref:codegen.adoc[]
1112
* xref:graphiql.adoc[]
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
[[federation]]
2+
= Federation
3+
4+
Spring for GraphQL provides a small integration layer for the
5+
https://github.com/apollographql/federation-jvm[federation-jvm] library that in turn builds
6+
on GraphQL Java, and helps to initialize the `graphql.schema.GraphQLSchema` for a GraphQL
7+
Java application that is a sub-graph within a federated graph. For more details, see
8+
https://www.apollographql.com/docs/federation/[Apollo Federation] and the
9+
https://www.apollographql.com/docs/federation/subgraph-spec[Subgraph spec].
10+
11+
To use the support you can declare a `FederationSchemaFactory` bean in your config, and plug
12+
it into `GraphQlSource.Builder`. In a Spring Boot application you can do this through a
13+
`GraphQlSourceBuilderCustomizer` as follows:
14+
15+
[source,java,indent=0,subs="verbatim,quotes"]
16+
----
17+
@Configuration
18+
public class FederationConfig {
19+
20+
@Bean
21+
public FederationSchemaFactory schemaFactory() {
22+
return new FederationSchemaFactory();
23+
}
24+
25+
@Bean
26+
public GraphQlSourceBuilderCustomizer customizer(FederationSchemaFactory factory) {
27+
return builder -> builder.schemaFactory(factory::createGraphQLSchema);
28+
}
29+
30+
}
31+
----
32+
33+
Now your sub-graph schema can extend federated types:
34+
35+
[source,graphql,indent=0,subs="verbatim,quotes"]
36+
----
37+
type Book @key(fields: "id") @extends {
38+
id: ID! @external
39+
author: Author
40+
}
41+
42+
type Author {
43+
id: ID
44+
firstName: String
45+
lastName: String
46+
}
47+
----
48+
49+
To assist with resolving federated types as part of an
50+
https://www.apollographql.com/docs/federation/subgraph-spec/#understanding-query_entities[_entities]
51+
query, you can use `@EntityMapping` methods side by side with `@SchemaMapping` methods
52+
for the data that the subgraph application owns. For example:
53+
54+
[source,java,indent=0,subs="verbatim,quotes"]
55+
----
56+
@Controller
57+
private static class BookController {
58+
59+
@EntityMapping
60+
public Book book(@Argument int id) {
61+
// ...
62+
}
63+
64+
@SchemaMapping
65+
public Author author(Book book) {
66+
// ...
67+
}
68+
69+
}
70+
----
71+
72+
The `@Argument` method parameters is resolved from the "representation"input map for the entity.
73+
You can also inject the full `Map<String, Object>`. The below shows all supported arguments:
74+
75+
[cols="1,2"]
76+
|===
77+
| Method Argument | Description
78+
79+
| `@Argument`
80+
| For access to a named value from the "representation" input map, also converted to typed Object.
81+
82+
| `Map<String, Object>`
83+
| The full "representation" input map for the entity.
84+
85+
| `@ContextValue`
86+
| For access to an attribute from the main `GraphQLContext` in `DataFetchingEnvironment`.
87+
88+
| `@LocalContextValue`
89+
| For access to an attribute from the local `GraphQLContext` in `DataFetchingEnvironment`.
90+
91+
| `GraphQLContext`
92+
| For access to the context from the `DataFetchingEnvironment`.
93+
94+
| `java.security.Principal`
95+
| Obtained from the Spring Security context, if available.
96+
97+
| `@AuthenticationPrincipal`
98+
| For access to `Authentication#getPrincipal()` from the Spring Security context.
99+
100+
| `DataFetchingFieldSelectionSet`
101+
| For access to the selection set for the query through the `DataFetchingEnvironment`.
102+
103+
| `Locale`, `Optional<Locale>`
104+
| For access to the `Locale` from the `DataFetchingEnvironment`.
105+
106+
| `DataFetchingEnvironment`
107+
| For direct access to the underlying `DataFetchingEnvironment`.
108+
109+
|===
110+
111+
`@EntityMapping` methods can return `Mono`, `CompletableFuture`, `Callable`, or the actual entity.
112+
113+
You can use `@GraphQlExceptionHandler` methods to map exceptions from `@EntityMapping`
114+
methods to ``GraphQLError``'s. The errors will be included in the response of the
115+
"_entities" query. Exception handler methods can be in the same controller or in an
116+
`@ControllerAdvice` class.
117+

spring-graphql-docs/modules/ROOT/pages/request-execution.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ locations, e.g. across multiple modules.
6767

6868
By default, `GraphQlSource.Builder` uses the GraphQL Java `SchemaGenerator` to create the
6969
`graphql.schema.GraphQLSchema`. This works for typical use, but if you need to use a
70-
different generator, e.g. for federation, you can register a `schemaFactory` callback:
70+
different generator, e.g. for xref:federation.adoc[federation], you can register a
71+
`schemaFactory` callback:
7172

7273
[source,java,indent=0,subs="verbatim,quotes"]
7374
----

spring-graphql/src/test/java/org/springframework/graphql/data/federation/EntityMappingInvocationTests.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,12 @@ private static class AuthorController {
155155

156156
@Nullable
157157
@EntityMapping
158-
public Book book(@Argument int id) {
158+
public Book book(@Argument int id, Map<String, Object> map) {
159+
160+
assertThat(map).hasSize(2)
161+
.containsEntry("__typename", Book.class.getSimpleName())
162+
.containsEntry("id", String.valueOf(id));
163+
159164
return switch (id) {
160165
case -97 -> throw new IllegalArgumentException("handled");
161166
case -98 -> throw new IllegalStateException("not handled");

0 commit comments

Comments
 (0)