Skip to content

Commit ed6b7a6

Browse files
authored
Upgrade to Spring for GraphQL 1.3 and use built-in federation support (#80)
Spring for GraphQL 1.3 adds [built-in federation support](https://spring.io/blog/2024/02/21/spring-for-graphql-1-3-m1-released#apollo-federation) with annotated, controller methods. This PR contains the changes to switch to the built-in support. Also some minor refactoring. I know 1.3 is not due until May 21, and Spring Boot 3.3 until May 23, but I wanted to have the changes ready and make sure all works as expected. For now this is based on release candidates.
1 parent f88fbb2 commit ed6b7a6

File tree

16 files changed

+122
-103
lines changed

16 files changed

+122
-103
lines changed

products-subgraph/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import org.springframework.boot.gradle.tasks.bundling.BootJar
22

33
plugins {
4-
id("org.springframework.boot") version "3.1.4"
4+
id("org.springframework.boot") version "3.3.0-RC1"
55
id("io.spring.dependency-management") version "1.1.3"
66
java
77
}
@@ -11,6 +11,7 @@ version = "0.0.1-SNAPSHOT"
1111

1212
repositories {
1313
mavenCentral()
14+
maven("https://repo.spring.io/milestone")
1415
}
1516

1617
val federation_jvm_version: String = project.property("federation-jvm.version").toString()

products-subgraph/src/main/java/com/example/products/GraphQLConfiguration.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

products-subgraph/src/main/java/com/example/products/ProductsApplication.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.graphql.data.federation.FederationSchemaFactory;
58

69
@SpringBootApplication
710
public class ProductsApplication {
@@ -10,4 +13,14 @@ public static void main(String[] args) {
1013
SpringApplication.run(ProductsApplication.class, args);
1114
}
1215

16+
@Bean
17+
public GraphQlSourceBuilderCustomizer customizer(FederationSchemaFactory factory) {
18+
return builder -> builder.schemaFactory(factory::createGraphQLSchema);
19+
}
20+
21+
@Bean
22+
FederationSchemaFactory federationSchemaFactory() {
23+
return new FederationSchemaFactory();
24+
}
25+
1326
}
Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,25 @@
11
package com.example.products;
22

3+
import java.util.List;
4+
35
import com.example.products.model.Product;
6+
import com.example.products.model.ProductSource;
7+
48
import org.springframework.graphql.data.method.annotation.Argument;
59
import org.springframework.graphql.data.method.annotation.QueryMapping;
610
import org.springframework.stereotype.Controller;
711

8-
import java.util.List;
9-
import java.util.Map;
10-
import java.util.stream.Collectors;
11-
import java.util.stream.Stream;
12-
1312
@Controller
1413
public class ProductsController {
1514

16-
private final Map<String, Product> PRODUCTS = Stream.of(
17-
new Product("1","Saturn V", "The Original Super Heavy-Lift Rocket!"),
18-
new Product("2","Lunar Module"),
19-
new Product("3","Space Shuttle"),
20-
new Product("4","Falcon 9", "Reusable Medium-Lift Rocket"),
21-
new Product("5","Dragon", "Reusable Medium-Lift Rocket"),
22-
new Product("6","Starship", "Super Heavy-Lift Reusable Launch Vehicle")
23-
).collect(Collectors.toMap(Product::id, product -> product));
24-
2515
@QueryMapping
26-
public Product product(@Argument String id) {
27-
return PRODUCTS.get(id);
16+
public Product product(@Argument Long id) {
17+
return ProductSource.getProduct(id);
2818
}
2919

3020
@QueryMapping
3121
public List<Product> products() {
32-
return PRODUCTS.values().stream().toList();
22+
return ProductSource.getProducts();
3323
}
24+
3425
}

products-subgraph/src/main/java/com/example/products/model/Product.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* description: String
88
* }
99
*/
10-
public record Product(String id, String name, String description) {
10+
public record Product(Long id, String name, String description) {
1111

12-
public Product(String id, String name) {
12+
public Product(Long id, String name) {
1313
this(id, name, null);
1414
}
1515
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.example.products.model;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
import java.util.stream.Collectors;
6+
7+
public final class ProductSource {
8+
9+
private static final List<Product> productList = List.of(
10+
new Product(1L, "Saturn V", "The Original Super Heavy-Lift Rocket!"),
11+
new Product(2L, "Lunar Module"),
12+
new Product(3L, "Space Shuttle"),
13+
new Product(4L, "Falcon 9", "Reusable Medium-Lift Rocket"),
14+
new Product(5L, "Dragon", "Reusable Medium-Lift Rocket"),
15+
new Product(6L, "Starship", "Super Heavy-Lift Reusable Launch Vehicle")
16+
);
17+
18+
private static final Map<Long, Product> productMap =
19+
productList.stream().collect(Collectors.toMap(Product::id, product -> product));
20+
21+
public static Product getProduct(Long id) {
22+
return productMap.get(id);
23+
}
24+
25+
public static List<Product> getProducts() {
26+
return productList;
27+
}
28+
29+
}

products-subgraph/src/main/resources/application.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ spring:
22
graphql:
33
graphiql:
44
enabled: true
5+
logging:
6+
level:
7+
org.springframework.graphql: DEBUG

products-subgraph/src/test/java/com/example/products/ProductsApplicationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ query ProductById($productId: ID!) {
3333
.execute()
3434
.path("product")
3535
.entity(Product.class)
36-
.isEqualTo(new Product("1","Saturn V", "The Original Super Heavy-Lift Rocket!"));
36+
.isEqualTo(new Product(1L,"Saturn V", "The Original Super Heavy-Lift Rocket!"));
3737
}
3838
}

reviews-subgraph/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import org.springframework.boot.gradle.tasks.bundling.BootJar
22

33
plugins {
4-
id("org.springframework.boot") version "3.1.4"
4+
id("org.springframework.boot") version "3.3.0-RC1"
55
id("io.spring.dependency-management") version "1.1.3"
66
java
77
}
@@ -11,6 +11,7 @@ version = "0.0.1-SNAPSHOT"
1111

1212
repositories {
1313
mavenCentral()
14+
maven("https://repo.spring.io/milestone")
1415
}
1516

1617
val federation_jvm_version: String = project.property("federation-jvm.version").toString()

reviews-subgraph/src/main/java/com/example/reviews/GraphQLConfiguration.java

Lines changed: 0 additions & 44 deletions
This file was deleted.

reviews-subgraph/src/main/java/com/example/reviews/ReviewsApplication.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.boot.autoconfigure.graphql.GraphQlSourceBuilderCustomizer;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.graphql.data.federation.FederationSchemaFactory;
58

69
@SpringBootApplication
710
public class ReviewsApplication {
@@ -10,4 +13,14 @@ public static void main(String[] args) {
1013
SpringApplication.run(ReviewsApplication.class, args);
1114
}
1215

16+
@Bean
17+
public GraphQlSourceBuilderCustomizer customizer(FederationSchemaFactory factory) {
18+
return builder -> builder.schemaFactory(factory::createGraphQLSchema);
19+
}
20+
21+
@Bean
22+
FederationSchemaFactory federationSchemaFactory() {
23+
return new FederationSchemaFactory();
24+
}
25+
1326
}
Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
package com.example.reviews;
22

3-
import com.example.reviews.model.Review;
3+
import java.util.List;
4+
45
import com.example.reviews.model.Product;
6+
import com.example.reviews.model.Review;
7+
import com.example.reviews.model.ReviewSource;
8+
9+
import org.springframework.graphql.data.federation.EntityMapping;
10+
import org.springframework.graphql.data.method.annotation.Argument;
511
import org.springframework.graphql.data.method.annotation.SchemaMapping;
612
import org.springframework.stereotype.Controller;
713

8-
import java.util.Collections;
9-
import java.util.List;
10-
import java.util.Map;
11-
1214
@Controller
1315
public class ReviewsController {
1416

15-
private final Map<String, List<Review>> REVIEWS = Map.of(
16-
"2", List.of(new Review("1020", "Very cramped :( Do not recommend.", 2), new Review("1021", "Got me to the Moon!", 4)),
17-
"3", List.of(new Review("1030", 3)),
18-
"4", List.of(new Review("1040", 5), new Review("1041", "Reusable!", 5), new Review("1042", 5)),
19-
"5", List.of(new Review("1050", "Amazing! Would Fly Again!", 5), new Review("1051", 5))
20-
);
17+
@EntityMapping
18+
public Product product(@Argument Long id) {
19+
return new Product(id);
20+
}
2121

2222
@SchemaMapping
23-
public List<Review> reviews(Product show) {
24-
return REVIEWS.getOrDefault(show.id(), Collections.emptyList());
23+
public List<Review> reviews(Product product) {
24+
return ReviewSource.getReviews(product);
2525
}
26+
2627
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package com.example.reviews.model;
22

3-
public record Product(String id) {
3+
public record Product(Long id) {
44
public static final String PRODUCT_TYPE = "Product";
55
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.example.reviews.model;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.Map;
6+
7+
public final class ReviewSource {
8+
9+
private static final Map<Long, List<Review>> reviewMap = Map.of(
10+
2L, List.of(new Review("1020", "Very cramped :( Do not recommend.", 2), new Review("1021", "Got me to the Moon!", 4)),
11+
3L, List.of(new Review("1030", 3)),
12+
4L, List.of(new Review("1040", 5), new Review("1041", "Reusable!", 5), new Review("1042", 5)),
13+
5L, List.of(new Review("1050", "Amazing! Would Fly Again!", 5), new Review("1051", 5))
14+
);
15+
16+
public static List<Review> getReviews(Product product) {
17+
return reviewMap.getOrDefault(product.id(), Collections.emptyList());
18+
}
19+
20+
}

reviews-subgraph/src/main/resources/application.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ spring:
55
graphql:
66
graphiql:
77
enabled: true
8-
8+
schema:
9+
printer:
10+
enabled: true
11+
logging:
12+
level:
13+
org.springframework.graphql: DEBUG

settings.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
pluginManagement {
2+
repositories {
3+
mavenCentral()
4+
gradlePluginPortal()
5+
maven("https://repo.spring.io/milestone")
6+
}
7+
}
8+
19
rootProject.name = "federation-jvm-spring-example"
210

311
include(":products-subgraph")

0 commit comments

Comments
 (0)