Oppbygging av federerte subgrafer
Utvikle individuelle Spring Boot-tjenester som federerte subgrafer, og definer skjemaet og entitetsrelasjonene deres.
Oppbygging av federerte subgrafer er en gratis leksjon i GraphQL-API-er med Spring Boot på CoddyKit. Dette er leksjon 2 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i GraphQL-API-er med Spring Boot, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i GraphQL-API-er med Spring Boot inneholder totalt 4 leksjoner.
Introduksjon til fødererte subgrafer
I GraphQL Federation består en supergraf av flere uavhengige GraphQL-tjenester, kalt subgrafer.
Hver subgraf er et selvstendig GraphQL-API som eier en bestemt del av domenemodellen din. Tenk på den som en mikrotjeneste for dataene dine.
Apollo Gateway kombinerer deretter disse subgrafene til ett samlet API, slik at klienter enkelt kan spørre etter data på tvers av ulike tjenester.
Forstå fødererte entiteter
Entiteter er kjernebegrepet for å koble sammen subgrafer. En entitet representerer en type som kan refereres til og utvides på tvers av ulike tjenester.
En User-entitet kan for eksempel defineres i en "Auth"-subgraf, men utvides av en "Product"-subgraf for å legge til brukerspesifikke anmeldelser.
Entiteter markeres i skjemaet ved hjelp av @key-direktivet, som angir hvordan en instans av typen identifiseres entydig.
Konfigurere subgrafen
For å bygge en føderert subgraf med Spring Boot starter du med et standard Spring Boot GraphQL-prosjekt.
Den viktigste avhengigheten er spring-boot-starter-graphql. Sørg også for å inkludere spring-boot-starter-web for HTTP-endepunkter.
Du må også konfigurere build.gradle eller pom.xml slik at GraphQL-skjemafiler (.graphqls) inkluderes.
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.5'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.coddykit'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-graphql'
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework:spring-graphql-test'
}
Skjema med @key-direktivet
@key-direktivet er avgjørende. Det forteller Apollo Gateway hvilke felt som identifiserer en entitet entydig i subgrafen.
Du kan definere flere nøkler eller sammensatte nøkler (for eksempel @key(fields: "id type")). Gateway bruker disse feltene til å hente delvise data fra andre subgrafer.
La oss definere en enkel Product-entitet og markere id som primærnøkkelen.
# src/main/resources/graphql/schema.graphqls
type Query {
products: [Product]
productById(id: ID!): Product
}
type Product @key(fields: "id") {
id: ID!
name: String!
price: Float!
}Bygge Product-entiteten
Nå skal vi implementere Product-entiteten i Spring Boot-applikasjonen vår. Dette innebærer å opprette en dataklasse og en resolver.
Resolveren håndterer spørringer etter produkter, på samme måte som ethvert annet GraphQL-endepunkt. For enkelhets skyld bruker vi data lagret i minnet.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import java.util.List;
import java.util.ArrayList;
// Assume schema.graphqls is defined as in Scene 4
@SpringBootApplication
public class ProductSubgraphApplication {
public static void main(String[] args) {
SpringApplication.run(ProductSubgraphApplication.class, args);
}
@Controller
public static class ProductResolver {
private static final List<Product> products = new ArrayList<>(List.of(
new Product("1", "Laptop", 1200.00),
new Product("2", "Mouse", 25.00)
));
@QueryMapping
public List<Product> products() {
return products;
}
@QueryMapping
public Product productById(@Argument String id) {
return products.stream()
.filter(p -> p.getId().equals(id))
.findFirst()
.orElse(null);
}
}
public static class Product {
private String id;
private String name;
private Double price;
public Product(String id, String name, Double price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getId() { return id; }
public String getName() { return name; }
public Double getPrice() { return price; }
}
}Implementere _entities-resolveren
Apollo Gateway trenger en måte å be subgrafen om en entitet ved hjelp av nøkkelen. Dette håndteres av en spesiell _entities-spørring.
I Spring Boot implementerer du dette ved å tilby en RuntimeWiringConfigurer-bean. Denne bean-en kobler _entities-spørringen til en datahenter.
Datahenteren mottar «representations» (kart med __typename og nøkkelfelt) og skal returnere de tilsvarende entitetsobjektene.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import org.springframework.stereotype.Controller;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.ArrayList;
// Assume schema.graphqls and Product/ProductResolver from Scene 5
@SpringBootApplication
public class ProductSubgraphApplication { // Same as previous scene
public static void main(String[] args) {
SpringApplication.run(ProductSubgraphApplication.class, args);
}
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
return builder -> builder.type("Query", typeWiring ->
typeWiring.dataFetcher("_entities", env -> {
List<Map<String, Object>> representations = env.getArgument("representations");
return representations.stream()
.map(representation -> {
if ("Product".equals(representation.get("__typename"))) {
String id = (String) representation.get("id");
// In a real app, fetch from DB by ID
return new Product(id, "Product " + id + " (Federated)", 0.0);
}
return null;
})
.collect(Collectors.toList());
})
);
}
// ProductResolver and Product class from Scene 5 would be here
@Controller
public static class ProductResolver {
private static final List<Product> products = new ArrayList<>(List.of(
new Product("1", "Laptop", 1200.00),
new Product("2", "Mouse", 25.00)
));
@QueryMapping public List<Product> products() { return products; }
@QueryMapping public Product productById(String id) {
return products.stream().filter(p -> p.getId().equals(id)).findFirst().orElse(null);
}
}
public static class Product {
private String id; private String name; private Double price;
public Product(String id, String name, Double price) { this.id=id; this.name=name; this.price=price; }
public String getId() { return id; } public String getName() { return name; }
public Double getPrice() { return price; }
}
}Utvide en entitet du eier
Hvis subgrafen din eier en entitet (det vil si at den definerte @key), kan du legge til flere felt ved å oppdatere skjemaet og resolveren.
De nye feltene blir tilgjengelige direkte fra subgrafen. Gateway vet at spørringer etter disse feltene skal rutes til tjenesten din.
La oss legge til et description-felt i Product-entiteten.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;
import org.springframework.context.annotation.Bean;
import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.ArrayList;
// src/main/resources/graphql/schema.graphqls (updated)
// type Product @key(fields: "id") {
// id: ID!
// name: String!
// price: Float!
// description: String # NEW FIELD
// }
// ... (rest of schema and _entities resolver as before)
@SpringBootApplication
public class ProductSubgraphApplication { // Same as previous scene
public static void main(String[] args) {
SpringApplication.run(ProductSubgraphApplication.class, args);
}
@Controller
public static class ProductResolver {
private static final List<Product> products = new ArrayList<>(List.of(
new Product("1", "Laptop", 1200.00, "Powerful computing device."),
new Product("2", "Mouse", 25.00, "Ergonomic wireless mouse.")
));
@QueryMapping public List<Product> products() { return products; }
@QueryMapping public Product productById(String id) {
return products.stream().filter(p -> p.getId().equals(id)).findFirst().orElse(null);
}
}
public static class Product {
private String id; private String name; private Double price; private String description; // NEW FIELD
public Product(String id, String name, Double price, String description) {
this.id = id; this.name = name; this.price = price; this.description = description;
}
public String getId() { return id; } public String getName() { return name; }
public Double getPrice() { return price; } public String getDescription() { return description; }
}
// runtimeWiringConfigurer bean would be here as in Scene 6
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer() {
return builder -> builder.type("Query", typeWiring ->
typeWiring.dataFetcher("_entities", env -> {
List<Map<String, Object>> representations = env.getArgument("representations");
return representations.stream()
.map(representation -> {
if ("Product".equals(representation.get("__typename"))) {
String id = (String) representation.get("id");
// Fetch full product data based on ID
return new Product(id, "Product " + id, 0.0, "Placeholder desc.");
}
return null;
})
.collect(Collectors.toList());
})
);
}
}Utvide eksterne entiteter
Hva om en annen subgraf definerer Product, men subgrafen din (for eksempel en "Review"-tjeneste) ønsker å legge til anmeldelser?
Du bruker @extends-direktivet i typedefinisjonen. Dette forteller Gateway at typen er en utvidelse av en entitet som er definert et annet sted.
Du bruker også @external på felt som er en del av den opprinnelige entitetsdefinisjonen, men som subgrafen trenger for å løse de nye feltene.
# src/main/resources/graphql/schema.graphqls (Review Subgraph)
type Query {
reviews: [Review]
}
type Review {
id: ID!
text: String!
productId: ID!
}
# This subgraph extends the Product type from another service
extend type Product @key(fields: "id") {
id: ID! @external
reviews: [Review] # New field added by THIS subgraph
}Løse utvidede felt
Når subgrafen din utvider en entitet, må du implementere en resolver for de nye feltene du har lagt til (for eksempel reviews på Product).
Spring GraphQL bruker @SchemaMapping-annotasjonen til dette. Metoden mottar foreldreobjektet (Product-instansen), som vil inneholde @external-feltene.
Gateway sender id (vårt @external-felt) til resolveren, slik at du kan hente de relevante anmeldelsene.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.graphql.data.method.annotation.SchemaMapping; // Important for extensions
import org.springframework.stereotype.Controller;
import org.springframework.context.annotation.Bean;
import graphql.schema.idl.RuntimeWiring;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.ArrayList;
// Assume schema.graphqls is defined as in Scene 8
@SpringBootApplication
public class ReviewSubgraphApplication {
public static void main(String[] args) {
SpringApplication.run(ReviewSubgraphApplication.class, args);
}
@Controller
public static class ReviewResolver {
private static final List<Review> reviews = new ArrayList<>(List.of(
new Review("101", "Great laptop!", "1"),
new Review("102", "Mouse works well.", "2"),
new Review("103", "Solid performance.", "1")
));
@QueryMapping
public List<Review> reviews() {
return reviews;
}
// Resolver for the 'reviews' field on the extended Product type
@SchemaMapping(typeName = "Product")
public List<Review> reviews(Product product) { // Product object will have its 'id'
return reviews.stream()
.filter(r -> r.getProductId().equals(product.getId()))
.collect(Collectors.toList());
}
}
public static class Review {
private String id; private String text; private String productId;
public Review(String id, String text, String productId) {
this.id = id; this.text = text; this.productId = productId;
}
public String getId() { return id; } public String getText() { return text; }
public String getProductId() { return productId; }
}
// Product class for extensions (only needs @external fields)
public static class Product {
private String id; // This field is received from Gateway
public Product(String id) { this.id = id; }
public String getId() { return id; }
}
// _entities resolver for Review type (if Review is also an entity) would be here if needed
}Sjekk av direktiver
Federation er i stor grad avhengig av bestemte direktiver for å definere hvordan subgrafer samhandler. Test forståelsen din av disse viktige direktivene.
Oppsummering av bygging av subgrafer
Du har lært å bygge fødererte subgrafer i Spring Boot!
- Definere entiteter med
@key-direktivet. - Implementere
_entities-resolvere slik at Gateway kan hente entiteter. - Legge til felt i entiteter som eies av subgrafen din.
- Utvide entiteter fra andre subgrafer ved hjelp av
@extendsog@external. - Implementere resolvere for disse utvidede feltene ved hjelp av
@SchemaMapping.
Disse byggeklossene gjør det mulig å lage modulære og skalerbare GraphQL-API-er med Federation.
Lær deg GraphQL-API-er med Spring Boot med en AI-veileder – gratis
Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.
- Kurs
- 12
- Leksjoner
- 48
Ofte stilte spørsmål
Er leksjonen «Oppbygging av federerte subgrafer» gratis?
Ja – hele teksten i «Oppbygging av federerte subgrafer» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av GraphQL-API-er med Spring Boot-kurset, kan du oppgradere til CoddyKit PRO. Kurset i GraphQL-API-er med Spring Boot inneholder totalt 4 leksjoner.
Hva lærer jeg i «Oppbygging av federerte subgrafer»?
Utvikle individuelle Spring Boot-tjenester som federerte subgrafer, og definer skjemaet og entitetsrelasjonene deres. Du øver på GraphQL-API-er med Spring Boot med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.
Trenger jeg erfaring for å begynne med GraphQL-API-er med Spring Boot?
Ingen tidligere erfaring er nødvendig. GraphQL-API-er med Spring Boot på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 2 av 4.
Hvor lang tid tar leksjonen «Oppbygging av federerte subgrafer»?
De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.
Kan jeg skrive og kjøre kode i denne GraphQL-API-er med Spring Boot-leksjonen?
Ja. Alle GraphQL-API-er med Spring Boot-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.
Alle leksjonene i dette kurset
- Introduksjon til Apollo Federation
- Oppbygging av federerte subgrafer
- Oppsett og administrasjon av Gateway
- Entitetsreferanser og @key-direktivet