Java-Anwendungen mit GraphQL, Teil 1

Seite 5: Mit Field-Resolvern Daten vom Domain Model ermitteln

Inhaltsverzeichnis

Um das Verhalten zu verändern, lassen sich für einzelne Felder sogenannte Field-Resolver schreiben. Sie liefern den Wert für ein Feld an einem konkreten Object Type aus dem Schema. Das ergibt beispielsweise Sinn, wenn das POJO nicht über ein Feld verfügt, dass im Schema definiert ist oder das Feld von einem anderen (Java-)Typen als im Schema ist.

Field-Resolver kann man ähnlich wie Root-Resolver implementieren. Zunächst ist dafür das Interface GraphQLResolver hinzuzufügen. Das Interface erwartet als Type-Parameter die Java-Klasse, für die es Felder auflösen soll. Die Methoden in der Klasse müssen wieder den Feldnamen aus dem Schema entsprechen. Als Parameter übergibt das Framework zunächst das Objekt, auf dem das Feld ermittelt werden soll. Als weitere Parameter kommen die im Schema definierten Argumente hinzu.

Im Beispiel gibt es das Feld averageStars zwar im Schema am Object Type Beer, aber nicht an der entsprechenden Java-Klasse. Um das Feld über GraphQL anzubieten, implementiert man dafür einen Field-Resolver.

public class BeerFieldResolver implements GraphQLResolver<Beer> {
  public int averageStars(Beer beer) {
    return (int)
      Math.round(
        beer.getRatings().
          stream().mapToDouble(Rating::getStars).
          average().getAsDouble()
      );
  }
}

Der BeerQueryResolver liefert zur Laufzeit zunächst eine Instanz des Beer-Objekts zurück (Einstiegspunkt des Query, entweder über das Feld beer oder beers). Wenn der Client daraufhin das Feld averageStars abfragt, ruft das die gleichnamige Methode BeerFieldResolver auf. Die vom Root-Resolver zurückgelieferte Beer-Instanz ist der erste übergebene Parameter, der zweite der vom Client angegebene Parameter stars. Auch der Resolving-Prozess funktioniert auf jeder Ebene der Abfrage. Wenn entweder das Beer-Objekt oder der BeerResolver ein weiteres POJO zurückliefert (zum Beispiel Rating), können Entwickler hierfür wieder individuelle Field-Resolver schreiben. Gibt es für ein Feld keinen Field Resolver, liest die Anwendung den Wert per Reflection aus dem Objekt.