<img height="1" width="1" src="https://www.facebook.com/tr?id=1272750026111903&amp;ev=PageView &amp;noscript=1">

GraphQL på Spring Boot med Java - overraskende lett!

Karl Jørgen Overå | 29. januar 2019

Har du hatt lyst å komme i gang med GraphQL på Spring Boot med Java? Kanskje du til og med har en eksisterende Spring Boot server du har lyst å utvide med GraphQL? Det er faktisk overraskende lett!

GraphQL

Skjema-først

"Schema-first" i kontekst av GraphQL betyr noe så enkelt som at man bygger skjemaet sitt først - altså hvordan dataene skal gjøres tilgjengelig og struktureres - deretter implementasjon.
I praksis betyr det at vi skriver GraphQL-skjema vårt i .graphlqls-filer, deretter implementerer vi tilhørende resolvere som Java-kode. Når vi starter serveren syes dette sammen til et API.

Oppsett og konfigurering

Fra dette punktet og fremover antas det at du har en Spring Boot server kjørende. Har du ikke det, men har fortsatt lyst å følge med fremover kan du raskt starte et prosjekt ved å bruke Spring Initializr.

Det er i hovedsak to biblioteker vi skal bruke i dag som gjør jobben vår ved å legge til GraphQL på Spring Boot veldig lett:

graphql-java-kickstart/graphql-spring-boot-starter 
graphql-java-kickstart/graphiql-spring-boot-starter

Dokumentasjon til disse bibliotekene.

Vi må legge til disse som avhengigheter i build.gradle.

Grapql_blog_1.gradle

For å skru disse på så må vi skru litt på vår application.yml (eller .properties):

Grapql_blog_2.yml

Nå kan du starte serveren og besøke URL-en http://localhost:8080/graphiql, da vil du bli møtt med noe som ser sånn ut, dette er GraphiQL-konsellen. GraphiQL er til GraphQL som Swagger er til REST. Dette er skjemaet vi bruker til å teste spørringer mens vi utvikler.

GraphiQL

Ved det punktet her er det ikke så veldig interessant, fordi du vil kun få en 404-feilmelding. Det er fordi først må vi konfigurere vårt GraphQL-skjema.

Skjema-først gjør livet lett

Nå kunne vi begynt å konfigurere skjemaet vårt som ren Java-kode. Men det er mer tungvint, vanskelig å holde oversikt og blir som oftest mye boilerplate. Vi velger derfor å skille skjemaet og koden vår, og gi ansvaret om å sy alt sammen til et bibliotek.

For å oppnå dette vil vi bruke et veldig bra bibliotek som heter graphql-java-kickstart/graphql-java-tools.

La oss begynne med å legge til biblioteket til build.gradle.

Github_blog_3.gradle

Så oppretter vi en fil ved navn schema.graphqls i resources, dersom du ønsker kan du godt legge denne i en mappe.

Her kan vi endelig begynne å definere vårt GraphQL-skjema.

Github_blog_4.graphql

Nå må vi bare implementere en tilsvarende “resolver” i Java-koden vår. Så vi lager en ny fil ved navn RootQueryResolver.java:

graphql_blogg_5_java

Dette er ikke noe mer avansert enn en klasse som implementerer GraphQLQueryResolver, som har en funksjon som stemmer overens med skjemaet vårt. Funksjonen kunne også ha hett hello(). graphql-java-tools leter etter begge navnene.

Husk å ha @Component over klassen så Spring Boot vet at den eksisterer.

Nå kan du starte serveren igjen, gå tilbake til http://localhost:8080/graphiql, så kan vi prøve å eksekvere vår første query:

graphiql_2

Hurra! Dersom du har gjort alt rett skal du få tilbake “Hello World”.

Dersom du får en feil ved oppstart som sier noe om NoClassDefFoundException så er det fordi Spring Boot overskriver Kotlin-versjonen som biblioteket bruker.

Det kan enkelt løses ved å legge til en verdi i gradle.properties:

Kotlin version

Dette problemet skal være løst når Spring Boot 2.2 kommer.

Er du et kodetalent? Se ledige stillinger her

Utvide skjemaet

Nå kan vi utforske hvordan vi kan bygge videre på dette. La oss begynne med å legge til en ekstra “query ” i rotskjemaet.

Grapql_blog_6.graphql

For å lære litt raskt om debugging, start serveren uten å ha implementert en resolver for bye.

Du vil se en stack-trace som er ganske lang, men den viktige informasjonen burde se sånn ut:

graphql_blogg_7

Her ser vi at så fort skjemaet er utvidet med noe nytt, så vil graphql-java-tools begynne å se etter tilhørende resolver i alle klasser som implementerer et av GraphQLQueryResolver, GraphQLMutationResolver eller GraphQLResolver<T> interfacene. Dersom den ikke finner det den vil ha ved oppstart så vil den tryne.

Nå kan vi implementere den. Oppdater RootQueryResolver med følgende kode:

graphql_blogg_8_java

Restarter du serveren kan du nå prøve å spørre etter bye i tillegg til hello i GraphiQL!

Objekter i GraphQL

Litt av det som gjør GraphQL kraftig er at man kan definere en kompleks datastruktur med mange forskjellige relasjoner. Vi utvider skjemaet med en ekstra type.

Graphql_blog_9.graphql

Legger du merke til at age: Int ikke har et utropstegn på slutten? Det betyr at verdien kan være null. Du kan gå dypere i GraphQL-skjema typing her.

Nå må vi oppdatere query resolveren med en funksjon som henter karl-feltet. Dette eksempelet bruker lombok. Det er ikke påkrevd men det gjør koden ganske mye kortere, ekstra fint til blogger! Ønsker du å ikke bruke lombok så er det bare å lage en POJO med gettere og settere.

graphql_blogg_10_java

Restart serveren, gå tilbake til GraphiQL og kjør denne spørringen:

Graphql_blog11.graphql

graphiql_3

Du får tilbake et objekt med flere felter!

Parametere

Å spørre etter karl direkte er jo veldig upraktisk. La oss endre karl til person også legger vi til et parameter i samme slengen.

Graphql_blog_11.1_graphql

Når vi legger til parametere i skjemaet er det så enkelt som å ha dem som en del av signaturen i Java-funksjonen.

graphql_blogg_12_java

Restart serveren igjen og prøv den nye spørringen. I dette eksempelet kjører jeg to spørringer samtidig ved å gi dem hvert sitt navn.

GraphiQL_4

Resolving av verdier på objekter

Nå som vi har et Person-objekt som kan spørres etter. La oss utvide det objektet med et felt som kun hentes dersom det er etterspurt i spørringen.

Vi legger til en complexValuePerson-objektet.

Grapql_blog_13.graphql

Nå kan vi opprette en fil som heter PersonResolver.java.

Her implementerer vi resolveren som graphql-java-tools forventer å finne.

graphql_blogg_14_java

Når man definerer objekter som resolvere, må man implementere et interface basert på data-objektet man returnerer til GraphQL. I dette tilfellet implementerer vi GraphQLResolver<Person>. Det betyr at alle resolvere vi definerer her vil motta en instans av Person som allerede har blitt hentet, som parameter. Dette objektet kan vi bruke til å hente andre verdier, i eksempelet over bruker vi navnet, men kunne like gjerne vært en unik id.

Restart serveren, og test den nye spørringen:

GraphiQL_5

Se litt nærmere på loggen til serveren:

Fetching value

Her ser vi at selv om vi har to spørringer, så blir funksjonen getComplexValue kun invokert èn gang, og det for den spørringen som ber om den verdien. Man kan jo tenke seg at dersom det er et eksternt kall bak denne verdien så vil det jo være en merkbar forskjell på disse to spørringene.

Mutasjoner

I GraphQL har spørringer (queries) kun lov å hente data. Trenger du å persistere eller endre noe data må du bruke en “mutation”. Mutasjoner er definert nesten helt likt som en spørring, men funksjonene må implementeres under en GraphQLMutationResolver istedenfor.

Vi kan begynne med å legge til en mutasjon i skjemaet vårt.

Graphql_blog_15.graphql

Så kan vi opprette vår GraphQLMutationResolver ved navn RootMutationResolver.java:

graphql_blogg_16_java

Denne klassen har en funksjon newPerson som matcher signaturen til GraphQL-skjemaet. Implementasjonen er enkel, den oppretter et nytt objekt, persisterer det og sender det tilbake. Mutasjoner kan returnere samme type komplekse objekter som spørringer! Inkludert verdier som må resolves.

PersonDatabase-implementasjonen er bare en fasade. Det er bare en ArrayList, hvor elementer legges til og leses ut fra. Kilde på Github.

GraphiQL_6

Oppdaterer vi RootQueryResolver til å lese fra PersonDatabase (eksempel på github) kan vi både skrive og lese med API-et vårt!

GraphiQL_7

Håper du lærte noe nyttig. Har du noen spørsmål eller kommentarer så er det bare å sende en epost til karl.overaa@systek.no.

Ønsker du å gå enda dypere i GraphQL etter dette anbefaler jeg graphql.org/learn.

Kildekoden til denne bloggposten finner du på karl-run/graphql-blogpost på Github.

Er du nysgjerrig på en karriere i Systek? Se våre ledige stillinger

Del på sosiale medier: