Slide 1

Slide 1 text

GraphQL NILS HARTMANN JAVA USER GROUP HH | AUGUST 2018 | @NILSHARTMANN Slides: https://bit.ly/jughh-graphql für Java-Anwendungen

Slide 2

Slide 2 text

@NILSHARTMANN NILS HARTMANN Programmierer aus Hamburg JavaScript, TypeScript, React Java Trainings, Workshops [email protected]

Slide 3

Slide 3 text

GraphQL "GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data" - https://graphql.org

Slide 4

Slide 4 text

GRAPHQL Spezifikation: https://facebook.github.io/graphql/ • 2015 von Facebook erstmals veröffentlicht • Query Sprache und -Ausführung • Schema Definition Language • Nicht: Implementierung • Referenz-Implementierung: graphql-js

Slide 5

Slide 5 text

GRAPHQL GraphQL != SQL • kein SQL, keine "vollständige" Query-Sprache • z.B. keine Sortierung, keine (beliebigen) Joins etc • keine Datenbank! • kein Framework!

Slide 6

Slide 6 text

GRAPHQL GraphQL != Mainstream • Implementierungen und Einsatz noch "bleeding edge" • Wenig erprobte Best-Practices

Slide 7

Slide 7 text

GRAPHQL GraphQL != Mainstream • Implementierungen und Einsatz noch "bleeding edge" • Wenig erprobte Best-Practices • ...dennoch wird es von einigen verwendet!

Slide 8

Slide 8 text

GITHUB https://twitter.com/github/status/866590967314472960

Slide 9

Slide 9 text

GITLAB (ALPHA) https://docs.gitlab.com/ee/api/graphql/

Slide 10

Slide 10 text

ATLASSIAN https://docs.atlassian.com/atlassian-confluence/1000.1829.0/overview-summary.html !

Slide 11

Slide 11 text

TWITTER https://twitter.com/tgvashworth/status/862049341472522240

Slide 12

Slide 12 text

NEW YORK TIMES https://open.nytimes.com/react-relay-and-graphql-under-the-hood-of-the-times-website-redesign-22fb62ea9764

Slide 13

Slide 13 text

GraphQL praktisch Source-Code: https://bit.ly/jughh-graphql-example

Slide 14

Slide 14 text

Demo: GraphiQL http://localhost:9000

Slide 15

Slide 15 text

Demo: IDE Support Beispiel: Intellij IDEA

Slide 16

Slide 16 text

Demo: IDE Support Beispiel: VS Code

Slide 17

Slide 17 text

Vergleich mit REST

Slide 18

Slide 18 text

BEERADVISOR DOMAINE "Domain-Model"

Slide 19

Slide 19 text

ABFRAGEN MIT REST REST-Zugriff • Exemplarisch und vereinfacht GET /beer/1 { "id": "1", "name": "Barfüßer" }

Slide 20

Slide 20 text

ABFRAGEN MIT REST REST-Zugriff • Exemplarisch und vereinfacht GET /beer/1 GET /beer/1/rating/R1 { "id": "1", "name": "Barfüßer" } { "id": "R1", "author": "U1", "stars": 3, "comment": "good!" }

Slide 21

Slide 21 text

ABFRAGEN MIT REST REST-Zugriff • Pro Entität (Resource) eine Abfrage • Zurückgeliefert wird immer komplette Resource • Keine Gesamt-Sicht auf Domaine GET /beer/1 GET /beer/1/rating/R1 GET /user/U1 { "id": "1", "name": "Barfüßer" } { "id": "R1", "author": "U1", "stars": 3, "comment": "good!" } { "id": "U1", "name": "Klaus", }

Slide 22

Slide 22 text

ABFRAGEN MIT GRAPHQL GraphQL query { beer { name ratings(rid: "R1") { stars author { name } } } { "name": "Barfüßer", "ratings": { "stars": 3, "comment: "good", "author": { "name": "Klaus" } } }

Slide 23

Slide 23 text

ABFRAGEN MIT GRAPHQL GraphQL • Beliebige Abfragen über veröffentliches Domain Model / API • Kein Widerspruch zu REST, kann als Ergänzung genutzt werden • z.B. Login oder File Upload { "name": "Barfüßer", "ratings": { "stars": 3, "comment: "good", "author": { "name": "Klaus" } } }

Slide 24

Slide 24 text

EINSATZSZENARIEN Gründe für den Einsatz von GraphQL • Viele unterschiedliche Use-Cases, die unterschiedliche Daten benötigen • Unterschiedliche Ansichten im Frontend • Unterschiedliche Clients • Einheitliche Gesamt-Sicht auf Domaine erwünscht • Typ-sichere API erfordert • Im Gegensatz zu REST (mehr) standardisiert

Slide 25

Slide 25 text

EINSATZSZENARIEN • Gateway für Frontend zu mehreren Backends

Slide 26

Slide 26 text

GraphQL TEIL 1: ABFRAGEN UND SCHEMA "GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data" - https://graphql.org

Slide 27

Slide 27 text

QUERY LANGUAGE Credits: https://dev-blog.apollodata.com/the-anatomy-of-a-graphql-query-6dffa9e9e747 • Strukturierte Sprache, um Daten von der API abzufragen • Abgefragt werden Felder von (verschachtelten) Objekten { beer { id name ratings { stars comment } } } Fields

Slide 28

Slide 28 text

QUERY LANGUAGE Credits: https://dev-blog.apollodata.com/the-anatomy-of-a-graphql-query-6dffa9e9e747 • Strukturierte Sprache, um Daten von der API abzufragen • Abgefragt werden Felder von (verschachtelten) Objekten • Felder können Argumente haben { beer(beerId: "B1") { id name ratings { stars comment } } } Fields Arguments

Slide 29

Slide 29 text

QUERY LANGUAGE Ergebnis • Identische Struktur wie bei der Abfrage { beer(beerId: "B1") { id name ratings { stars comment } } } "data": { "beer": { "id": "B1" "name": "Barfüßer" "ratings": [ { "stars": 3, "comment": "grate taste" }, { "stars": 5, "comment": "best beer ever!" } ] } }

Slide 30

Slide 30 text

QUERY LANGUAGE: OPERATIONS Credits: https://dev-blog.apollodata.com/the-anatomy-of-a-graphql-query-6dffa9e9e747 Operation: beschreibt, was getan werden soll • query, mutation, subscription query GetMeABeer { beer(beerId: "B1") { id name price } } Operation type Operation name (optional)

Slide 31

Slide 31 text

QUERY LANGUAGE: OPERATIONS Credits: https://dev-blog.apollodata.com/the-anatomy-of-a-graphql-query-6dffa9e9e747 Operation: Variablen query GetMeABeer($bid: ID!) { beer(beerId: $bid) { id name price } } Variable Definition Variable usage

Slide 32

Slide 32 text

QUERY LANGUAGE: MUTATIONS Beispiel: Mutation • Mutation wird zum Verändern von Daten verwendet • Entspricht POST, PUT, PATCH, DELETE in REST • Rückgabe Wert kann frei definiert werden (z.B. neue Entität) mutation AddRatingMutation($input: AddRatingInput!) { addRating(input: $input) { id beerId author comment } } "input": { beerId: "B1", author: "Nils", comment: "YEAH!" } Operation type Operation name (optional) Variable Definition Variable Object

Slide 33

Slide 33 text

QUERY LANGUAGE: MUTATIONS Beispiel: Subscription • Automatische Benachrichtigung bei neuen Daten subscription NewRatingSubscription { newRating: onNewRating { id beerId author comment } } Operation type Operation name (optional) Field alias

Slide 34

Slide 34 text

QUERIES AUSFÜHREN Queries werden über HTTP ausgeführt • Üblicherweise per POST • Ein einzelner Endpoint, z.B. /graphql $ curl -X POST -H "Content-Type: application/json" \ -d '{"query":"{ beers { name } }"}' \ http://localhost:9000/graphql {"data": {"beers": [ {"name": "Barfüßer"}, {"name": "Frydenlund"}, {"name": "Grieskirchner"}, {"name": "Tuborg"}, {"name": "Baltic Tripple"}, {"name": "Viktoria Bier"} ]} }

Slide 35

Slide 35 text

QUERIES AUSFÜHREN Queries werden über HTTP ausgeführt • Beispiel: IDEA HTTP Client Editor

Slide 36

Slide 36 text

QUERIES AUSFÜHREN Antwort vom Server • Grundsätzlich HTTP 200 • (JSON-)Map mit max drei Feldern { {"errors": [ { "message": "Could not read User with ID 123", "locations": [ . . . ], "path": [ "beer", "ratings", "author" ] } ] }, {"data": {"beers": [ . . . ] }, {"extensions": { . . . } }

Slide 37

Slide 37 text

GRAPHQL SCHEMA Schema • Eine GraphQL API muss mit einem Schema beschrieben werden • Schema legt fest, welche Types und Fields es gibt • Eine Möglichkeit: Schema Definition Language (SDL) • In der GraphQL Spec seit 2018

Slide 38

Slide 38 text

GRAPHQL SCHEMA Schema Definition per SDL type Rating { id: ID! comment: String! stars: Int } Object Type Fields

Slide 39

Slide 39 text

GRAPHQL SCHEMA Schema Definition per SDL type Rating { id: ID! comment: String! stars: Int } Return Type (non-nullable) Return Type (nullable)

Slide 40

Slide 40 text

GRAPHQL SCHEMA Schema Definition per SDL type Rating { id: ID! comment: String! stars: Int author: User! } type User { id: ID! name: String! } Return Type (references other Type)

Slide 41

Slide 41 text

GRAPHQL SCHEMA Schema Definition per SDL type Rating { id: ID! comment: String! stars: Int author: User! } type User { id: ID! name: String! } type Beer { name: String! ratings: [Rating!]! } Return Type (List/Array)

Slide 42

Slide 42 text

GRAPHQL SCHEMA Schema Definition per SDL type Rating { id: ID! comment: String! stars: Int author: User! } type User { id: ID! name: String! } type Beer { name: String! ratings: [Rating!]! ratingsWithStars(stars: Int!): [Rating!]! } Arguments

Slide 43

Slide 43 text

GRAPHQL SCHEMA Root-Types: Einstiegspunkte in die API (Query) type Query { beers: [Beer!]! beer(beerId: ID!): Beer } Root-Type ("Query")

Slide 44

Slide 44 text

GRAPHQL SCHEMA Root-Types: Einstiegspunkte in die API (Query) type Query { beers: [Beer!]! beer(beerId: ID!): Beer } Root-Type Root-Fields

Slide 45

Slide 45 text

GRAPHQL SCHEMA Root-Types: Einstiegspunkte in die API (Query, Mutation) type Query { beers: [Beer!]! beer(beerId: ID!): Beer } type Mutation { addRating(newRating: NewRating): Rating! } Root-Type ("Mutation")

Slide 46

Slide 46 text

GRAPHQL SCHEMA Root-Types: Einstiegspunkte in die API (Query, Mutation) type Query { beers: [Beer!]! beer(beerId: ID!): Beer } type Mutation { addRating(newRating: NewRating): Rating! } input NewRating { authorId: ID! comment: String! } Input-Object (für komplexe Argumente)

Slide 47

Slide 47 text

GRAPHQL SCHEMA Root-Types: Einstiegspunkte in die API (Query, Mutation, Subscription) type Query { beers: [Beer!]! beer(beerId: ID!): Beer } type Mutation { addRating(newRating: NewRating): Rating! } input NewRating { authorId: ID! comment: String! } type Subscription { onNewRating: Rating! } Root-Type (Subscription)

Slide 48

Slide 48 text

QUERIES AUSFÜHREN Schema Evolution: Es gibt nur eine Version der API • Schema kann abwärtskompatibel verändert werden • zum Beispiel neue Typen, neue Felder • Felder und Typen können deprecated werden type Query { beer: [Beer!]! beer(beerId: ID!): Beer allBeers: [Beer!]! @deprecated(reason: "Use 'beer' field") } Directive

Slide 49

Slide 49 text

GRAPHQL SCHEMA Schema: Instrospection • Root-Felder "__schema" und "__type" (Beispiel) query { __type(name: "Beer") { name kind description fields { name description type { ofType { name } } } } } { "data": { "__type": { "name": "Beer", "kind": "OBJECT", "description": "Representation of a Beer", "fields": [ { "name": "id", "description": "Id for this Beer", "type": { "ofType": { "name": "ID" } } }, { "name": "price", "description": "Price of the beer", "type": { "ofType": { "name": "Int" } } }, ... ] } }

Slide 50

Slide 50 text

TEIL 2: RUNTIME-UMGEBUNG (AKA: DEINE ANWENDUNG) GraphQL für Java "GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data" - https://graphql.org

Slide 51

Slide 51 text

GRAPHQL-JAVA PROJEKT FAMILIE graphql-java Low-Level API SDL Implementierung https://github.com/graphql-java

Slide 52

Slide 52 text

GRAPHQL-JAVA PROJEKT FAMILIE graphql-java-tools High-Level API insbesondere für Resolver graphql-java Low-Level API SDL Implementierung https://github.com/graphql-java

Slide 53

Slide 53 text

GRAPHQL-JAVA PROJEKT FAMILIE graphql-java-servlet Servlet für GraphQL Requests graphql-java-tools High-Level API insbesondere für Resolver graphql-java Low-Level API SDL Implementierung https://github.com/graphql-java

Slide 54

Slide 54 text

GRAPHQL-JAVA PROJEKT FAMILIE graphql-java-servlet Servlet für GraphQL Requests graphql-spring-boot-starter Abstraktion für Spring Boot (Einlesen Schemas, Servlet Registrierung,...) graphql-java-tools High-Level API insbesondere für Resolver graphql-java Low-Level API SDL Parser https://github.com/graphql-java

Slide 55

Slide 55 text

SCHEMA Schema definieren • Inline oder extern per .graphqls-Datei type User { id: ID! login: String! name: String! } type Rating { id: ID! beer: Beer! author: User! comment: String! stars: Int! } type Beer { id: ID! name: String! price: String! ratings: [Rating!]! ratingsWithStars(stars: Int!): [Rating!]! } type Query { beer(beerId: ID!): Beer beers: [Beer!]! } input AddRatingInput { beerId: ID! userId: ID! comment: String! stars: Int! } type Mutation { addRating(ratingInput: AddRatingInput): Rating! } graphql-java- tools

Slide 56

Slide 56 text

RESOLVER graphql-java- tools Resolver implementieren • Ein Resolver liefert ein Wert für ein angefragtes Feld • Default: per Reflection • Root-Resolver (für Query-Typ) müssen implementiert werden

Slide 57

Slide 57 text

RESOLVER graphql-java- tools Resolver implementieren • Beispiel: Root-Resolver (Query) type Query { beers: [Beer!]! } import com.coxautodev.graphql.tools.GraphQLQueryResolver; public class RatingQueryResolver implements GraphQLQueryResolver { } Interface für Query-Resolver

Slide 58

Slide 58 text

RESOLVER graphql-java- tools Resolver implementieren • Beispiel: Root-Resolver (Query) type Query { beers: [Beer!]! } import com.coxautodev.graphql.tools.GraphQLQueryResolver; public class RatingQueryResolver implements GraphQLQueryResolver { // z.B via DI private BeerRepository beerRepository; public List beers() { return beerRepository.findAll(); } } Zuordnung über Namenskonvention (getXyz etc auch erlaubt)

Slide 59

Slide 59 text

RESOLVER graphql-java- tools Resolver implementieren • Beispiel: Root-Resolver (Query) type Query { beers: [Beer!]! beer(beerId: ID!): Beer } import com.coxautodev.graphql.tools.GraphQLQueryResolver; public class RatingQueryResolver implements GraphQLQueryResolver { // z.B via DI private BeerRepository beerRepository; public List beers() { return beerRepository.findAll(); } public Beer beer(String beerId) { return beerRepository.getBeer(beerId); } } Argumente als Parameter

Slide 60

Slide 60 text

RESOLVER graphql-java- tools Resolver implementieren • Beispiel: Root-Resolver (Mutation) • Ähnlich wie Query-Resolver import com.coxautodev.graphql.tools.GraphQLMutationResolver; public class RatingMutationResolver implements GraphQLMutationResolver { } type Mutation { addRating (ratingInput: AddRatingInput): Rating! }

Slide 61

Slide 61 text

RESOLVER graphql-java- tools Resolver implementieren • Beispiel: Root-Resolver (Mutation) • Ähnlich wie Query-Resolver import com.coxautodev.graphql.tools.GraphQLMutationResolver; public class RatingMutationResolver implements GraphQLMutationResolver { // z.B via DI private RatingRepository ratingRepository; public Rating addRating(AddRatingInput newRating) { } } type Mutation { addRating (ratingInput: AddRatingInput): Rating! }

Slide 62

Slide 62 text

RESOLVER graphql-java- tools Resolver implementieren • Beispiel: Root-Resolver (Mutation) • Ähnlich wie Query-Resolver import com.coxautodev.graphql.tools.GraphQLMutationResolver; public class RatingMutationResolver implements GraphQLMutationResolver { // z.B via DI private RatingRepository ratingRepository; public Rating addRating(AddRatingInput newRating) { Rating rating = Rating.from(newRating); ratingRepository.save(rating); return rating; } } type Mutation { addRating (ratingInput: AddRatingInput): Rating! }

Slide 63

Slide 63 text

RESOLVER graphql-java- tools Resolver implementieren • Beispiel: Root-Resolver (Mutation) • Input-Typ kann normales POJO sein public class AddRatingInput { private String beerId; private String userId; private String comment; private int stars; // ... getter und setter ... } input AddRatingInput { beerId: ID! userId: ID! comment: String! stars: Int! }

Slide 64

Slide 64 text

RESOLVING ZUR LAUFZEIT • Schritt 1: Root Resolver graphql-java- tools

Slide 65

Slide 65 text

RESOLVING ZUR LAUFZEIT • Schritt 2: Field Resolver (Default: Reflection) graphql-java- tools

Slide 66

Slide 66 text

RESOLVING ZUR LAUFZEIT • Schritt 3: Field Resolver (Default: Reflection) graphql-java- tools

Slide 67

Slide 67 text

RESOLVING ZUR LAUFZEIT Problem: Mismatch zwischen Java-Klassen und Schema graphql-java- tools

Slide 68

Slide 68 text

RESOLVING ZUR LAUFZEIT Field Resolver für zusätzliche Felder • Explizite Zugriffs-Methoden für Felder, die nicht an Klasse vorhanden sind • Gilt auch für Felder, die zwischen API und Java-Klasse abweichen • z.B. anderer Rückgabe-Wert graphql-java- tools

Slide 69

Slide 69 text

FIELD RESOLVER graphql-java- tools Field Resolver implementieren • Liefert Wert für ein Feld in einem Objekt zurück import com.coxautodev.graphql.tools.GraphQLResolver; public class BeerFieldResolver implements GraphQLResolver { } type Beer { ratingsWithStars(stars: Int!): [Rating!]! }

Slide 70

Slide 70 text

FIELD RESOLVER graphql-java- tools Field Resolver implementieren • Liefert Wert für ein Feld in einem Objekt zurück import com.coxautodev.graphql.tools.GraphQLResolver; public class BeerFieldResolver implements GraphQLResolver { public List ratingsWithStars(Beer beer, int stars) { } } type Beer { ratingsWithStars(stars: Int!): [Rating!]! } Argumente als Parameter Quell-Objekt als Parameter

Slide 71

Slide 71 text

FIELD RESOLVER graphql-java- tools Field Resolver implementieren • Liefert Wert für ein Feld in einem Objekt zurück import com.coxautodev.graphql.tools.GraphQLResolver; public class BeerFieldResolver implements GraphQLResolver { public List ratingsWithStars(Beer beer, int stars) { return beer.getRatings().stream() .filter(r -> r.getStars() == stars) .collect(Collectors.toList()); } } type Beer { ratingsWithStars(stars: Int!): [Rating!]! }

Slide 72

Slide 72 text

RESOLVER graphql-java- tools com.coxautodev.graphql.tools.FieldResolverError: No method found with any of the following signatures (with or without one of [interface graphql.schema.DataFetchingEnvironment] as the last argument), in priority order: nh.graphql.beeradvisor.rating.graphql.RatingQueryResolver.beer(~beerId) nh.graphql.beeradvisor.rating.graphql.RatingQueryResolver.getBeer(~beerId) Validierung beim Starten • Alle Resolver müssen vorhanden sein • Return-Types und Methoden-Parameter der Resolver- Funktionen müssen zum Schema passen graphql-java

Slide 73

Slide 73 text

RESOLVER graphql-java- tools Validierung zur Laufzeit • Resolver werden immer mit korrekten Parametern aufgerufen • Argumente haben korrekten Typ • Argumente sind ggf. nicht null graphql-java

Slide 74

Slide 74 text

RESOLVER graphql-java- tools Validierung zur Laufzeit • Resolver werden immer mit korrekten Parametern aufgerufen • Argumente haben korrekten Typ • Argumente sind ggf. nicht null • Rückgabe-Wert eines Resolvers wird überprüft • Client erhält nie ungültige Werte graphql-java

Slide 75

Slide 75 text

RESOLVER graphql-java- tools Validierung zur Laufzeit • Resolver werden immer mit korrekten Parametern aufgerufen • Argumente haben korrekten Typ • Argumente sind ggf. nicht null • Rückgabe-Wert eines Resolvers wird überprüft • Client erhält nie ungültige Werte • Es werden nur Felder herausgegeben, die auch im Schema definiert sind • Alle anderen Felder einer Java-Klasse sind "unsichtbar" graphql-java

Slide 76

Slide 76 text

RUNTIME ERZEUGEN graphql-java- tools Schema-Instanz • GraphQLSchema verbindet SDL mit Resolvern import com.coxautodev.graphql.tools.SchemaParser; import graphql.schema.GraphQLSchema; public class BeerSchemaFactory { public static GraphQLSchema createSchema() { GraphQLSchema graphQLSchema = SchemaParser.newParser() .file("beer.graphqls") .resolvers(new BeerMutationResolver(), new BeerQueryResolver()) .build() .makeExecutableSchema(); } } graphql-java Schema-Datei Alle Resolver

Slide 77

Slide 77 text

QUERY AUSFÜHREN Query ausführen per API (low-level) import graphql.GraphQL; import graphql.schema.GraphQLSchema; GraphQLSchema beerSchema = BeerSchemaFactory.createSchema(); GraphQL graphQL = GraphQL.newGraphQL(beerSchema).build(); graphql-java Schema erzeugen Ausführungsumgebung erzeugen

Slide 78

Slide 78 text

QUERY AUSFÜHREN Query ausführen per API (low-level) import graphql.GraphQL; import graphql.schema.GraphQLSchema; import graphql.ExecutionInput; GraphQLSchema beerSchema = BeerSchemaFactory.createSchema(); GraphQL graphQL = GraphQL.newGraphQL(beerSchema).build(); ExecutionInput executionInput = ExecutionInput.newExecutionInput() .query ("{ beers { name ratings { author } } }") .build(); graphql-java Query definieren (hier könnten zB auch Argumente angegeben werden)

Slide 79

Slide 79 text

QUERY AUSFÜHREN Query ausführen per API (low-level) import graphql.GraphQL; import graphql.schema.GraphQLSchema; import graphql.ExecutionInput; import graphql.ExecutionResult; GraphQLSchema beerSchema = BeerSchemaFactory.createSchema(); GraphQL graphQL = GraphQL.newGraphQL(beerSchema).build(); ExecutionInput executionInput = ExecutionInput.newExecutionInput() .query ("{ beers { name ratings { author } } }") .build(); ExecutionResult executionResult = graphQL.execute(executionInput); Object data = executionResult.getData(); graphql-java Query ausführen Daten auslesen (oder Fehler) data: verschachtelte Map

Slide 80

Slide 80 text

QUERY AUSFÜHREN graphql-java- servlet Query ausführen per GraphQL Servlet import graphql.servlet.SimpleGraphQLHttpServlet; GraphQLSchema beerSchema = BeerSchemaFactory.createSchema(); SimpleGraphQLHttpServlet servlet = SimpleGraphQLHttpServlet .newBuilder(beerSchema) .build() // ... Servlet in Container anmelden ... http localhost:9000/graphql?query='{beers { name ratings { stars } } }' Query ausführen

Slide 81

Slide 81 text

MODULARISIERUNG Beispiel: mehrere Domainen

Slide 82

Slide 82 text

SCHEMA MODULARISIEREN Schema-Beschreibung auf mehrere Dateien verteilen • Neue Typen und Queries können definiert werden // shops.graphqls type Shop { id: ID! name: String! } extend type Query { shops: [Shop!]! } Neuer Object-Type Neues Root-Feld in Root-Type (GraphQLQueryResolver)

Slide 83

Slide 83 text

MODULARISIERUNG Beispiel: Referenzen zwischen Domainen

Slide 84

Slide 84 text

SCHEMA MODULARISIEREN Schema-Beschreibung • Bestehende Typen können weiter verwendet werden // shops.graphqls type Shop { id: ID! name: String! beers: [Beer!]! } extend type Query { shops: [Shop!]! } Verwenden eines Types aus anderem Schema

Slide 85

Slide 85 text

SCHEMA MODULARISIEREN Schema-Beschreibung • Bestehende Typen können weiter verwendet werden oder erweitert werden • Mehrere Field-Resolver für einen Type möglich // shops.graphqls type Shop { id: ID! name: String! beers: [Beer!]! } extend type Query { shops: [Shop!]! } extend type Beer { shops: [Shop!]! } Neues Feld in bestehendem "Beer" (GraphQLResolver)

Slide 86

Slide 86 text

SCHEMA MODULARISIEREN graphql-java- tools Schema-Instanz: mehrere Resolver und SDLs import com.coxautodev.graphql.tools.SchemaParser; import graphql.schema.GraphQLSchema; public class BeerSchemaFactory { public static GraphQLSchema createSchema() { GraphQLSchema graphQLSchema = SchemaParser.newParser() .file("beer.graphqls") .file("shop.graphqls") .resolvers(new BeerMutationResolver(), new BeerQueryResolver(), new ShopQueryResolver(), new ShopBeerFieldResolver()) .build().makeExecutableSchema(); } } graphql-java

Slide 87

Slide 87 text

ZUSAMMENFASSUNG GraphQL-Anwendung mit graphql-java graphql-java- servlet graphql-java- tools graphql-java

Slide 88

Slide 88 text

ZUSAMMENFASSUNG GraphQL-Anwendung mit graphql-java • Schema mit SDL beschreiben (*.graphqls) graphql-java- servlet graphql-java- tools graphql-java

Slide 89

Slide 89 text

ZUSAMMENFASSUNG GraphQL-Anwendung mit graphql-java • Schema mit SDL beschreiben (*.graphqls) • Root-Resolver (mindestens für Query) schreiben • Evtl Field-Resolver entwickeln graphql-java- servlet graphql-java- tools graphql-java

Slide 90

Slide 90 text

ZUSAMMENFASSUNG GraphQL-Anwendung mit graphql-java • Schema mit SDL beschreiben (*.graphqls) • Root-Resolver (mindestens für Query) schreiben • Evtl Field-Resolver entwickeln • GraphQLSchema mit SDL und Resolvern erzeugen • Flexible Konfiguration für viele Teile möglich (zB ObjectMapper) graphql-java- servlet graphql-java- tools graphql-java

Slide 91

Slide 91 text

ZUSAMMENFASSUNG GraphQL-Anwendung mit graphql-java • Schema mit SDL beschreiben (*.graphqls) • Root-Resolver (mindestens für Query) schreiben • Evtl Field-Resolver entwickeln • GraphQLSchema mit SDL und Resolvern erzeugen • Flexible Konfiguration für viele Teile möglich (zB ObjectMapper) • Servlet instantiieren und in Container deployen graphql-java- servlet graphql-java- tools graphql-java

Slide 92

Slide 92 text

SPRING BOOT STARTER Vereinfacht das Setup von GraphQL-Anwendungen graphql-spring- boot-starter

Slide 93

Slide 93 text

SPRING BOOT STARTER Vereinfacht das Setup von GraphQL-Anwendungen • Mergt alle Schema-Dateien im Klassenpfad zusammen (*.graphqls) graphql-spring- boot-starter

Slide 94

Slide 94 text

SPRING BOOT STARTER Vereinfacht das Setup von GraphQL-Anwendungen • Mergt alle Schema-Dateien im Klassenpfad zusammen (*.graphqls) • Resolver werden als Beans annotiert (zB. @Component) und automatisch dem Schema hinzugefügt graphql-spring- boot-starter

Slide 95

Slide 95 text

SPRING BOOT STARTER Vereinfacht das Setup von GraphQL-Anwendungen • Mergt alle Schema-Dateien im Klassenpfad zusammen (*.graphqls) • Resolver werden als Beans annotiert (zB. @Component) und automatisch dem Schema hinzugefügt • Servlet und Servlet-Mapping erfolgt per application.properties graphql-spring- boot-starter

Slide 96

Slide 96 text

SPRING BOOT STARTER Vereinfacht das Setup von GraphQL-Anwendungen • Mergt alle Schema-Dateien im Klassenpfad zusammen (*.graphqls) • Resolver werden als Beans annotiert (zB. @Component) und automatisch dem Schema hinzugefügt • Servlet und Servlet-Mapping erfolgt per application.properties graphql-spring- boot-starter # application.yaml graphql: servlet: mapping: /graphql enabled: true corsEnabled: true

Slide 97

Slide 97 text

SPRING BOOT STARTER Vereinfacht das Setup von GraphQL-Anwendungen • Mergt alle Schema-Dateien im Klassenpfad zusammen (*.graphqls) • Resolver werden als Beans annotiert (zB. @Component) und automatisch dem Schema hinzugefügt • Servlet und Servlet-Mapping erfolgt per application.properties • GraphiQL (API Explorer) kann ebenfalls per Konfiguration aktiviert werden graphql-spring- boot-starter # application.yaml graphql: servlet: mapping: /graphql enabled: true corsEnabled: true # application.yaml graphiql: mapping: /graphiql endpoint: /graphql enabled: true

Slide 98

Slide 98 text

SUBSCRIPTIONS • An Events (z.B. Datenänderungen) anmelden • Server informiert Client sobald es neue Events gibt • Kein Pendant in REST (?) graphql-java- tools

Slide 99

Slide 99 text

SUBSCRIPTIONS • An Events (z.B. Datenänderungen) anmelden • Server informiert Client sobald es neue Events gibt • Kein Pendant in REST (?) graphql-java- tools subscription { onNewRating { id beer { id } author { name } comment stars } } { "onNewRating": { "id": "R9", "beer": { "id": "B4" }, "author": { "name": "Lauren Jones" }, "comment": "Very tasteful", "stars": 4 } } Bei jedem neuen Rating

Slide 100

Slide 100 text

SUBSCRIPTIONS Schema Definition • Root-Type "Subscription" • Felder und Return-Values wie bei Query und Mutation graphql-java- tools type Subscription { onNewRating: Rating! newRatings(beerId: ID!): Rating! }

Slide 101

Slide 101 text

SUBSCRIPTIONS Resolver implementieren • Resolver ähnlich wie bei Query- und Mutation-Type import com.coxautodev.graphql.tools.GraphQLSubscriptionResolver; public class RatingMutationResolver implements GraphQLSubscriptionResolver { } type Subscription { onNewRating: Rating! } graphql-java- tools

Slide 102

Slide 102 text

SUBSCRIPTIONS Resolver implementieren • Resolver ähnlich wie bei Query- und Mutation-Type • Rückgabewert muss Reactive Streams Publisher sein import com.coxautodev.graphql.tools.GraphQLSubscriptionResolver; import org.reactivestreams.Publisher; public class RatingMutationResolver implements GraphQLSubscriptionResolver { // z.B via DI private RatingPublisher ratingPublisher; public Publisher onNewRating() { return ratingPublisher.getPublisher(); } } type Subscription { onNewRating: Rating! } graphql-java- tools

Slide 103

Slide 103 text

SUBSCRIPTIONS Publisher implementieren (Beispiel) • Hängt von verwendeter Technologie und Architektur ab • Beispiel: Spring type Subscription { onNewRating: Rating! } @Component public class RatingPublisher { public RatingPublisher() { // create reactivestreams Emitter and Publisher } @TransactionalEventListener public void ratingCreated(RatingCreatedEvent e) { this.emitter.onNext(e.getRating()); } public Publisher getPublisher() { return this.publisher; } } graphql-java- tools

Slide 104

Slide 104 text

SUBSCRIPTIONS graphql-java- servlet graphql-spring- boot-starter Veröffentlichen über WebSockets • Eigenes Servlet in graphql-java-servlet • Einbinden via Spring Boot Konfiguration # application.yaml graphql: servlet: websocket: enabled: true subscriptions: websocket: path: /subscriptions

Slide 105

Slide 105 text

HTTPS://NILSHARTMANN.NET | @NILSHARTMANN ! Vielen Dank! Beispiel-Code: https://bit.ly/jughh-graphql-example Slides: https://bit.ly/jughh-graphql

Slide 106

Slide 106 text

Ausblick: GraphQL Clients MIT APOLLO UND REACT

Slide 107

Slide 107 text

Apollo "React Apollo allows you to fetch data from your GraphQL server and use it in building ... UIs using the React framework." - https://github.com/apollographql/react-apollo

Slide 108

Slide 108 text

QUERIES import { gql } from "react-apollo"; const BEER_RATING_APP_QUERY = gql` query BeerRatingAppQuery { beers { id name price ratings { . . . } } } `; Queries – Daten vom Server lesen • Werden mittels gql-Funktion angegeben und geparst Query parsen

Slide 109

Slide 109 text

QUERIES import { gql, Query } from "react-apollo"; const BEER_RATING_APP_QUERY = gql`...`; const BeerRatingApp(props) => ( ); React Komponente Query-Komponente • Führt Query aus, kümmert sich um Caching, Fehlerbehandlung etc

Slide 110

Slide 110 text

QUERIES import { gql, Query } from "react-apollo"; const BEER_RATING_APP_QUERY = gql`...`; const BeerRatingApp(props) => ( {({ loading, error, data }) => { }} ); Query Ergebnis (wird ggf mehrfach aufgerufen) Query-Komponente • Führt Query aus, kümmert sich um Caching, Fehlerbehandlung etc • Ergebnis (Daten, ...) wird per Render-Prop (Children) übergeben

Slide 111

Slide 111 text

QUERIES import { gql, Query } from "react-apollo"; const BEER_RATING_APP_QUERY = gql`...`; const BeerRatingApp(props) => ( {({ loading, error, data }) => { if (loading) { return

Loading...

} if (error) { return

Error!

} return }} ); Query-Komponente • Führt Query aus, kümmert sich um Caching, Fehlerbehandlung etc • Ergebnis (Daten, ...) wird per Render-Prop (Children) übergeben Ergebnis (samt Fehler) auswerten

Slide 112

Slide 112 text

TYP-SICHERE VERWENDUNG import { BeerPageQueryResult, BeerPageQueryVars } from "..."; const BeerRatingPage(...) => ( query={BEER_PAGE_QUERY} variables={{bier: beerId}} > {({ loading, error, data }) => { // . . . return }} ); Beispiel TypeScript: Typ-sichere Queries Compile Fehler!

Slide 113

Slide 113 text

BEISPIEL: TYP-SICHERE QUERIES apollo-codegen: Generiert Typen für Flow und TypeScript • Schema wird vom Server geladen • Fehler in Queries werden schon beim Generieren erkannt $ apollo-codegen generate 'src/**/*.tsx' ... BeerRatingApp.tsx: Variable "$newBeerId" of type "String" used in position expecting type "ID". BeerPage.tsx: Cannot query field "alc" on type "Beer". Falscher Typ Unbekanntes Feld