Skip to content

Commit b8b4774

Browse files
committed
Merge branch '1.2.x'
2 parents a5448ff + 1d98f9b commit b8b4774

File tree

8 files changed

+224
-0
lines changed

8 files changed

+224
-0
lines changed

spring-graphql-docs/antora.yml

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ asciidoc:
1717
chomp: 'all'
1818
docs-site: https://docs.spring.io
1919
include-java: 'example$docs-src/main/java/org/springframework/graphql/docs'
20+
include-resources: 'example$docs-src/main/resources'
2021
github-tag: main
2122
github-repo: spring-projects/spring-graphql
2223
github-raw: https://raw.githubusercontent.com/{github-repo}/{github-tag}

spring-graphql-docs/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dependencies {
2525
api 'org.springframework:spring-messaging'
2626
api 'org.springframework.data:spring-data-commons'
2727
api 'com.querydsl:querydsl-core'
28+
api "org.springframework.boot:spring-boot-starter-graphql:${springBootVersion}"
2829
}
2930

3031
jar {

spring-graphql-docs/modules/ROOT/pages/controllers.adoc

+64
Original file line numberDiff line numberDiff line change
@@ -917,3 +917,67 @@ Supported return types are listed below:
917917
| For asynchronous resolution where `<T>` is one of the supported, synchronous, return types.
918918

919919
|===
920+
921+
922+
923+
[[controllers.namespacing]]
924+
== Namespacing
925+
926+
At the schema level, query and mutation operations are defined directly under the `Query` and `Mutation` types.
927+
Rich GraphQL APIs can define dozens of operation sunder those types, making it harder to explore the API and separate concerns.
928+
You can choose to https://www.apollographql.com/docs/technotes/TN0012-namespacing-by-separation-of-concern/[define Namespaces in your GraphQL schema].
929+
While there are some caveats with this approach, you can implement this pattern with Spring for GraphQL annotated controllers.
930+
931+
With namespacing, your GraphQL schema can, for example, nest query operations under top-level types, instead of listing them directly under `Query`.
932+
Here, we will define `MusicQueries` and `UserQueries` types and make them available under `Query`:
933+
934+
[source,json,subs="verbatim,quotes"]
935+
----
936+
include::ROOT:{include-resources}/controllers/namespaces.graphqls[]
937+
----
938+
939+
A GraphQL client would use the `album` query like this:
940+
941+
[source,graphql,subs="verbatim,quotes"]
942+
----
943+
{
944+
music {
945+
album(id: 42) {
946+
id
947+
title
948+
}
949+
}
950+
}
951+
----
952+
953+
And get the following response:
954+
955+
[source,json,subs="verbatim,quotes"]
956+
----
957+
{
958+
"data": {
959+
"music": {
960+
"album": {
961+
"id": "42",
962+
"title": "Spring for GraphQL"
963+
}
964+
}
965+
}
966+
}
967+
----
968+
969+
970+
This can be implemented in a `@Controller` with the following pattern:
971+
972+
include-code::MusicController[]
973+
<1> Annotate the controller with `@SchemaMapping` and a `typeName` attribute, to avoid repeating it on methods
974+
<2> Define a `@QueryMapping` for the "music" namespace
975+
<3> The "music" query returns an "empty" record, but could also return an empty map
976+
<4> Queries are now declared as fields under the "MusicQueries" type
977+
978+
Instead of declaring wrapping types ("MusicQueries", "UserQueries") explicitly in controllers,
979+
you can choose to configure them with the runtime wiring using a `GraphQlSourceBuilderCustomizer` with Spring Boot:
980+
981+
include-code::NamespaceConfiguration[]
982+
<1> List all the wrapper types for the "Query" type
983+
<2> Manually declare data fetchers for each of them, returning an empty Map
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2020-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.controllers.namespacing;
18+
19+
public record Album(String id, String title) {
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2020-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.controllers.namespacing;
18+
19+
public record Artist(String id, String name) {
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2020-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.controllers.namespacing;
18+
19+
import java.util.List;
20+
21+
import org.springframework.graphql.data.method.annotation.Argument;
22+
import org.springframework.graphql.data.method.annotation.QueryMapping;
23+
import org.springframework.graphql.data.method.annotation.SchemaMapping;
24+
import org.springframework.stereotype.Controller;
25+
26+
@Controller
27+
@SchemaMapping(typeName="MusicQueries") // <1>
28+
public class MusicController {
29+
30+
@QueryMapping // <2>
31+
public MusicQueries music() {
32+
return new MusicQueries();
33+
}
34+
35+
// <3>
36+
public record MusicQueries() {
37+
38+
}
39+
40+
@SchemaMapping // <4>
41+
public Album album(@Argument String id) {
42+
return new Album(id, "Spring GraphQL");
43+
}
44+
45+
@SchemaMapping
46+
public List<Artist> searchForArtist(@Argument String name) {
47+
return List.of(new Artist("100", "the Spring team"));
48+
}
49+
50+
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2020-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.docs.controllers.namespacing;
18+
19+
import java.util.Collections;
20+
import java.util.List;
21+
22+
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Configuration;
25+
26+
27+
@Configuration
28+
public class NamespaceConfiguration {
29+
30+
@Bean
31+
public GraphQlSourceBuilderCustomizer customizer() {
32+
List<String> queryWrappers = List.of("music", "users"); // <1>
33+
return sourceBuilder -> sourceBuilder.configureRuntimeWiring(wiringBuilder -> {
34+
queryWrappers.forEach(field -> wiringBuilder.type("Query",
35+
builder -> builder.dataFetcher(field, env -> Collections.emptyMap()))); // <2>
36+
});
37+
}
38+
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
type Query {
2+
music: MusicQueries
3+
users: UserQueries
4+
}
5+
6+
type MusicQueries {
7+
album(id: ID!): Album
8+
searchForArtist(name: String!): [Artist]
9+
}
10+
11+
type Album {
12+
id: ID!
13+
title: String!
14+
}
15+
16+
type Artist {
17+
id: ID!
18+
name: String!
19+
}
20+
21+
type UserQueries {
22+
user(login: String): User
23+
}
24+
25+
type User {
26+
id: ID!
27+
login: String!
28+
}

0 commit comments

Comments
 (0)