Kom godt i gang med GraphQL og Spring Boot

1. Introduktion

GraphQL er et relativt nyt koncept fra Facebook, der faktureres som et alternativ til REST til web-API'er.

Denne artikel giver en introduktion til opsætning af en GraphQL-server ved hjælp af Spring Boot, så den kan føjes til eksisterende applikationer eller bruges i nye.

2. Hvad er? GraphQL?

Traditionelle REST API'er arbejder med konceptet ressourcer, som serveren administrerer. Disse ressourcer kan manipuleres på nogle standardmåder ved at følge de forskellige HTTP-verb. Dette fungerer meget godt, så længe vores API passer til ressourcekonceptet, men falder hurtigt fra hinanden, når vi skal afvige fra det.

Dette lider også, når klienten har brug for data fra flere ressourcer på samme tid. For eksempel at anmode om et blogindlæg og kommentarer. Dette løses typisk ved enten at få klienten til at foretage flere anmodninger eller ved at få serveren til at levere ekstra data, der muligvis ikke altid er nødvendige, hvilket fører til større svarstørrelser.

GraphQL tilbyder en løsning på begge disse problemer. Det giver klienten mulighed for at specificere nøjagtigt, hvilke data der ønskes, herunder fra at navigere i underordnede ressourcer i en enkelt anmodning, og giver mulighed for flere forespørgsler i en enkelt anmodning.

Det fungerer også meget mere RPC ved hjælp af navngivne forespørgsler og mutationer i stedet for et standard obligatorisk sæt handlinger. Dette fungerer for at placere kontrollen, hvor den hører hjemme, hvor API-udvikleren angiver, hvad der er muligt, og API-forbrugeren, hvad der ønskes.

For eksempel kan en blog muligvis tillade følgende forespørgsel:

forespørgsel {recentPosts (count: 10, offset: 0) {id title category author {id name thumbnail}}}

Denne forespørgsel vil:

  • anmode om de ti seneste indlæg
  • for hvert indlæg, bed om ID, titel og kategori
  • for hvert indlæg anmoder forfatteren om at returnere ID, navn og miniaturebillede

I en traditionel REST API har dette enten brug for 11 anmodninger - 1 til stillingerne og 10 til forfatterne - eller skal inkludere forfatteroplysningerne i postoplysningerne.

2.1. GraphQL-skemaer

GraphQL-serveren udsætter et skema, der beskriver API'en. Denne ordning består af typedefinitioner. Hver type har et eller flere felter, som hver tager nul eller flere argumenter og returnerer en bestemt type.

Grafen består af den måde, hvorpå disse felter er indlejret med hinanden. Bemærk, at der ikke er behov for, at grafen er acyklisk - cyklusser er helt acceptable - men det er rettet. Det vil sige, klienten kan komme fra et felt til sine børn, men det kan ikke automatisk komme tilbage til forælderen, medmindre skemaet definerer dette eksplicit.

Et eksempel på GrafQL-skema for en blog kan indeholde følgende definitioner, der beskriver et indlæg, en forfatter af indlægget og en rodforespørgsel for at få de seneste indlæg på bloggen.

skriv Post {id: ID! titel: String! tekst: String! kategori: Stringforfatter: Forfatter! } skriv forfatter {id: ID! navn: String! miniaturebillede: Stringindlæg: [Indlæg]! } # Root Query for applikationstypen Query {recentPosts (count: Int, offset: Int): [Post]! } # Rootmutationen for applikationstypen Mutation {writePost (titel: String !, tekst: String !, category: String): Post! }

Det "!" i slutningen af ​​nogle navne angiver, at dette er en ikke-ugyldig type. Enhver type, der ikke har dette, kan være nul i svaret fra serveren. GraphQL-tjenesten håndterer disse korrekt, så vi kan anmode om underordnede felter af ugyldige typer sikkert.

GraphQL-tjenesten udsætter også selve skemaet ved hjælp af et standardsæt af felter, der gør det muligt for enhver klient at forespørge på skemadefinitionen på forhånd.

Dette kan give klienten mulighed for automatisk at registrere, når skemaet ændres, og give klienter mulighed for dynamisk at tilpasse sig den måde, skemaet fungerer på. Et utroligt nyttigt eksempel på dette er GraphiQL-værktøjet - diskuteret senere - der giver os mulighed for at interagere med enhver GraphQL API.

3. Introduktion til GraphQL Spring Boot Starter

Spring Boot GraphQL Starter tilbyder en fantastisk måde at få en GraphQL-server til at køre på meget kort tid. Kombineret med GraphQL Java Tools-biblioteket behøver vi kun at skrive den kode, der er nødvendig for vores service.

3.1. Opsætning af tjenesten

Alt, hvad vi har brug for, for at dette kan fungere, er de korrekte afhængigheder:

 com.graphql-java graphql-spring-boot-starter 5.0.2 com.graphql-java graphql-java-tools 5.2.4 

Spring Boot plukker automatisk disse op og indstiller de relevante håndterere til at arbejde automatisk.

Som standard udsætter dette GraphQL-tjenesten på / graphql slutpunkt for vores ansøgning og accepterer POST-anmodninger, der indeholder GraphQL-nyttelasten. Dette slutpunkt kan tilpasses i vores application.properties filen, hvis det er nødvendigt.

3.2. Skrivning af skemaet

GraphQL Tools-biblioteket fungerer ved at behandle GraphQL Schema-filer for at opbygge den korrekte struktur og derefter trække specielle bønner til denne struktur. Spring Boot GraphQL starter finder automatisk disse skemafiler.

Disse filer skal gemmes med udvidelsen “.graphqls”Og kan være til stede hvor som helst på klassestien. Vi kan også have så mange af disse filer som ønsket, så vi kan opdele ordningen i moduler efter ønske.

Det ene krav er, at der skal være nøjagtigt en rodforespørgsel og op til en rodmutation. Dette kan ikke opdeles på tværs af filer, i modsætning til resten af ​​ordningen. Dette er en begrænsning af selve definitionen af ​​GraphQL Schema og ikke af Java-implementeringen.

3.3. Root Query Resolver

Rodforespørgslen skal have specielle bønner defineret i Spring-sammenhæng for at håndtere de forskellige felter i denne rodforespørgsel. I modsætning til skemadefinitionen er der ingen begrænsning for, at der kun er en enkelt springbønne til rodforespørgselsfelterne.

De eneste krav er, at bønnerne implementeres GraphQLQueryResolver og at hvert felt i rodforespørgslen fra skemaet har en metode i en af ​​disse klasser med samme navn.

offentlig klasse Query implementerer GraphQLQueryResolver {private PostDao postDao; public List getRecentPosts (int count, int offset) {return postsDao.getRecentPosts (count, offset); }}

Metodens navne skal være en af ​​følgende i denne rækkefølge:

  1. is - kun hvis feltet er af typen Boolsk

Metoden skal have parametre, der svarer til alle parametre i GraphQL-skemaet, og kan eventuelt tage en endelig parameter af typen DataFetchingEnvironment.

Metoden skal også returnere den korrekte returtype for typen i GraphQL-skemaet, som vi er ved at se. Alle enkle typer - String, Int, List, osv. - kan bruges med de tilsvarende Java-typer, og systemet kortlægger dem bare automatisk.

Ovenstående definerede metoden getRecentPosts som vil blive brugt til at håndtere alle GraphQL - forespørgsler til Seneste indlæg felt i det skema, der er defineret tidligere.

3.4. Brug af bønner til at repræsentere typer

Hver kompleks type i GraphQL-serveren er repræsenteret af en Java-bønne - om det er indlæst fra rodforespørgslen eller hvor som helst andet sted i strukturen. Den samme Java-klasse skal altid repræsentere den samme GraphQL-type, men navnet på klassen er ikke nødvendig.

Felter inde i Java-bønnen kortlægges direkte på felter i GraphQL-svaret baseret på navnet på feltet.

offentlig klasse Post {privat streng-id; privat streng titel; privat streng kategori; private String authorId; }

Alle felter eller metoder på Java-bønnen, der ikke kortlægges til GraphQL-skemaet, ignoreres, men vil ikke forårsage problemer. Dette er vigtigt for feltopløsere at arbejde.

For eksempel marken authorId her svarer ikke til noget i vores skema, vi definerede tidligere, men det vil være tilgængeligt til næste trin.

3.5. Feltresolvere til komplekse værdier

Nogle gange er værdien af ​​et felt ikke trivielt at indlæse. Dette kan involvere databaseopslag, komplekse beregninger eller noget andet. GraphQL Tools har et koncept af en feltopløsning, der bruges til dette formål. Disse er foråret bønner, der kan give værdier i stedet for databønnen.

Feltopløseren er en hvilken som helst bønne i Spring Context, der har samme navn som databønnen, med suffikset Opløser, og det implementerer GraphQLResolver interface. Metoder på feltopløsningsbønnen følger alle de samme regler som på databønnen, men tilvejebringes også selve databønnen som en første parameter.

Hvis en feltopløsning og databønnen begge har metoder til det samme GraphQL-felt, vil feltopløseren have forrang.

offentlig klasse PostResolver implementerer GraphQLResolver {privat AuthorDao authorDao; public Author getAuthor (Post post) {return authorDao.getAuthorById (post.getAuthorId ()); }}

Det er vigtigt, at disse feltopløsere er indlæst fra forårssammenhæng. Dette giver dem mulighed for at arbejde med andre Spring-styrede bønner - fx DAO'er.

Vigtigere, Hvis klienten ikke anmoder om et felt, vil GraphQL Server aldrig gøre arbejdet for at hente det. Dette betyder, at hvis en klient henter et indlæg og ikke beder om forfatteren, så getAuthor () Metoden ovenfor vil aldrig blive udført, og DAO-opkaldet foretages aldrig.

3.6. Ubetydelige værdier

GraphQL-skemaet har det koncept, at nogle typer er ugyldige, og andre ikke er.

Dette kan håndteres i Java-koden ved direkte at bruge nulværdier, men ligeledes den nye Valgfri type fra Java 8 kan bruges direkte her til ugyldige typer, og systemet vil gøre det rigtige med værdierne.

Dette er meget nyttigt, da det betyder, at vores Java-kode er mere åbenlyst den samme som GraphQL-skemaet fra metodedefinitionerne.

3.7. Mutationer

Indtil videre har alt, hvad vi har gjort, handlet om at hente data fra serveren. GraphQL har også evnen til at opdatere de data, der er gemt på serveren, ved hjælp af mutationer.

Fra kodens synspunkt er der ingen grund til, at en forespørgsel ikke kan ændre data på serveren. Vi kunne let skrive forespørgselsopløsere, der accepterer argumenter, gemme nye data og returnere disse ændringer. At gøre dette vil medføre overraskende bivirkninger for API-klienterne og betragtes som dårlig praksis.

I stedet, Mutationer skal bruges til at informere klienten om, at dette vil medføre en ændring af de data, der lagres.

Mutationer defineres i Java-koden ved hjælp af klasser, der implementeres GraphQLMutationResolver i stedet for GraphQLQueryResolver.

Ellers gælder alle de samme regler som for forespørgsler. Returneringsværdien fra et mutationsfelt behandles derefter nøjagtigt den samme som fra et forespørgselsfelt, så også indlejrede værdier også kan hentes.

offentlig klasse Mutation implementerer GraphQLMutationResolver {privat PostDao postDao; public Post writePost (String title, String text, String category) {return postDao.savePost (title, text, category); }}

4. Introduktion til GraphiQL

GraphQL har også et ledsagende værktøj kaldet GraphiQL. Dette er et brugergrænseflade, der er i stand til at kommunikere med enhver GraphQL-server og udføre forespørgsler og mutationer mod den. En version, der kan downloades, findes som en Electron-app og kan hentes herfra.

Det er også muligt at inkludere den webbaserede version af GraphiQL i vores applikation automatisk ved at tilføje GraphiQL Spring Boot Starter-afhængighed:

 com.graphql-java graphiql-spring-boot-starter 5.0.2 

Dette fungerer kun, hvis vi er vært for vores GraphQL API på standardendepunktet for / graphql dog, så det er nødvendigt med den enkeltstående ansøgning, hvis det ikke er tilfældet.

5. Resume

GraphQL er en meget spændende ny teknologi, der potentielt kan revolutionere den måde, web-API'er udvikles på.

Kombinationen af ​​Spring Boot GraphQL Starter og GraphQL Java Tools-bibliotekerne gør det utroligt nemt at tilføje denne teknologi til alle nye eller eksisterende Spring Boot-applikationer.

Kodestykker kan findes på GitHub.