Versuchen Sie, einen GraphQL-Server mit grahpql-java-tools (+ kotlin) zu implementieren.

Was ist dieser Artikel?

Mit Kotlin + graphql-java-tools werden wir den Prozess der Implementierung des GraphQL-Servers und der Rückgabe einer Antwort zusammenfassen.

Einschließlich in diesem Artikel

Nicht in diesem Artikel enthalten

Über graphql-java-tools

Es bietet die Funktion als GraphQL-Server. Ein Tool namens graphql-spring-boot verwendet auch intern graphql-java-tools. Der GraphQL-Server selbst fungiert nicht als Webanwendungsserver. Sie müssen ein beliebiges Framework oder eine Bibliothek verwenden. In diesem Artikel werden wir Ktor verwenden.

Implementierungsablauf

  1. Definieren Sie das Schema für die graphql-Datei
  2. Implementieren Sie RootResolver
  3. Implementieren Sie Resolver
  4. Implementieren Sie einen Handler zur Verarbeitung von GraphQL-Anforderungen

Über den Unterschied zwischen Root Resolver und Resolver

graphql-java-tools hat zwei Arten von Resolver-Konzepten. (Resolver ist wie Controller in MVC)

Erwägen Sie die Implementierung des folgenden GraphQL-Schemas. Die Daten, in denen der Elternteil "Elternteil" das Kind "Kind" hat.

example.graphql


type Query {
  parent: Parent!
}

type Parent {
  id: ID!
  name: String!
  child: Child!
}

type Child {
  id: ID!
  name: String!
}

Wenn Sie Daten mit einer solchen Has A-Beziehung zu graphql-java-tools zurückgeben, müssen Sie zwei Typen implementieren: "RootResolver" und "Resolver". Die Rollen von jedem sind wie folgt.

RootResolver: Implementiert, was passiert, wenn auf das übergeordnete Element verwiesen wird. Wird mit einer Anfrage an den Endpunkt als Auslöser aufgerufen.

Resolver: Implementiert die Verarbeitung, wenn auf Parent.Child verwiesen wird. Es wird mit dem Zeitpunkt aufgerufen, zu dem die registrierte übergeordnete Klasse als Trigger aufgerufen wird.

Es ist schwer zu verstehen, deshalb erkläre ich es anhand eines Beispiels.

Angenommen, der in "exmple.graphql" definierte Typ wird wie folgt durch eine Kotlin-Klasse dargestellt.


data class Parent(
    val id: Int, 
    val name: String, 
    val childId: Int
)

data class Child(
    val id: Int,
    val name: String
)

Parent.child existiert nicht in der aktuellen Klasse. Um "Parent.child" als Antwort zurückzugeben, müssen Sie "Parent.childId" verwenden, um "Child" zuzuordnen. Dazu benötigen Sie "Resolver". Es kann das Objekt "Resolver" empfangen, wenn die angegebene Klasse als Antwort zurückgegeben wird. Daher können Sie auf childId verweisen, indem Sie Parent mit ParentResolver empfangen, und Sie können das zugehörige Child erhalten.

Zusammenfassung··· Verarbeitung, wenn auf den in Query definierten Endpunkt verwiesen wird-> RootResolver Was tun, wenn auf eine Eigenschaft eines vom Endpunkt zurückgegebenen Typs verwiesen wird-> Resolver?

1. Definieren Sie das Schema

Lassen Sie uns den Ablauf der tatsächlichen Implementierung des Servers sehen.

Definieren wir zunächst das GraphQL-Schema. Dieser Artikel behandelt nicht die Syntax von GraphQL. Informationen zur Syntax finden Sie in einem anderen Artikel.

Hier implementieren wir einen einfachen Abfrageprozess. Erstellen Sie eine Datei mit dem Namen sample.graphql in einem beliebigen Verzeichnis. Der diesmal erstellte Server liest die Definition dieser Datei.

Ich habe einen Endpunkt namens samples definiert, um Daten vom Sample-Typ als Liste zurückzugeben.

graphql/sample.graphql

type Query {
  samples: [Sample!]!
}

type Sample {
  id: ID
  name: String
  user: User
}

type User {
  id: ID
  sampleId: ID
}

Die entsprechende Datenklasse ist auch in Kotlin definiert.


data class Sample(
  val id: Int,
  val name: String,
  val userId: Int
)

data class User(
  val id: Int,
  val email: String
)

2. Implementieren Sie RootResolver

Wir werden RootResolver implementieren. Implementiert, was passiert, wenn die Endpunkt-Samples aufgerufen werden. Ursprünglich denke ich, dass UseCase usw. aufgerufen wird, aber dieses Mal werde ich es so machen, dass dieselbe Liste zurückgegeben wird.

RootSampleResolver.kt

class RootSampleResolver: GraphQLQueryResolver {
  fun samples(): List<Sample> {
    return listOf(
      Sample(id = 1, name = "sample1", userId = 1),
      Sample(id = 2, name = "sample2", userId = 2),
      Sample(id = 3, name = "sample3", userId = 3)
    )
  }
}

Es gibt zwei Punkte.

  1. Erben Sie GraphQLQueryResolver. Dieses Mal werden wir die Operation "Abfrage" implementieren, also erben wir "GraphQLQueryResolver". Erben Sie "GraphQLMutaionResolver", wenn Sie die Operation "Mutation" implementieren. Wenn beide Prozesse implementiert sind, kann ein "RootResolver" sowohl "GraphQLQueryResolver" als auch "GraphQLMutaionResolver" erben.

  2. Bereiten Sie eine Methode mit demselben Namen wie die in GraphQL definierte Abfrage vor. Da graphql / sample.graphql einen Abfrageendpunkt namens samples definiert, muss RootReposolver auch eine samples-Methode definieren. Wenn das Argument auf der GraphQL-Seite angegeben ist, muss zu diesem Zeitpunkt ein Argument des übereinstimmenden Typs in der Methode von RootReposolver empfangen werden.

3. Implementieren Sie Resolver

Wir werden SampleResolver implementieren. Implementiert die Verarbeitung, wenn die Eigenschaft von Sample.user aufgerufen wird.

Dieser SampleResolver wird ausgelöst, wenn die Sample-Klasse als Antwort zurückgegeben wird. Wie üblich wird UseCase nicht aufgerufen und gibt ein Objekt mit einer übereinstimmenden ID aus einer festen Liste zurück.

SampleResolver.kt

class SampleResolver: GraphQLResolver<Sample> {

  private val users = listOf(
    User(id = 1, email = "[email protected]"),
    User(id = 2, email = "[email protected]"),
    User(id = 3, email = "[email protected]")
  )
  
  fun user(input: Sample): User? {
    return users.find { it.id == input.userId }
  }
  
}

Es gibt zwei Punkte.

  1. Erben Sie "GraphQLResolver". Lassen Sie uns "GraphQLResolver" erben, indem wir das Objekt angeben, das Resolver in den Generika empfangen möchte. (In diesem Beispiel erben Sie "GraphqlResolver ".)

  2. Bereiten Sie eine Methode mit demselben Namen wie die in graphql definierte Eigenschaft vor Implementieren Sie die Methode "user", da sie die Verarbeitung implementiert, wenn auf "Sample.user" verwiesen wird, was in GraphQL definiert ist.

4. Implementieren Sie den Handler

Registrieren Sie die zuvor im Handler definierte Datei sample.graphql und den Resolver. Dadurch kann der GraphQL-Server Ihre Anfrage verarbeiten.

GraphqlHander.kt

class GraphQLHander {

  /**
  *Erstellen Sie das GraphQL-Schema.
  *Registrieren Sie das in filePath definierte Schema und den Resolver.
  **/
  fun init(): GraphQL {
    val filePath = "graphql/sample.graphql" 
    val schema = SchemaParser.newParser()
      //Lesen Sie die GraphQL-Definition
      .file(file)
      //Laden Sie den Resolver und ordnen Sie ihn dem GraphQL-Endpunkt zu
      .resolvers(listOf(RootSampleResolver(), SampleResolver())) 
      .build()
      .makeExecutableSchema()
  }

  /**
  *Verarbeitet GraphQL-Anforderungen.
  **/
  fun execute(query: String, operationName: String, variables: Map<String, Any>, context: Any): ExecutionResult {
    val graphql = init()
      return graphql.execute(
        ExecutionInput.newExecutionInput()
          .query(query)
          .operationName(operationName)
          .variables(variables)
          .context(context)
          .dataLoaderRegistry(dataLoaderProvider.provideDataLoaderRegistry())
      )
  }
}

Die GraphQL-Verarbeitung kann durchgeführt werden, indem der GraphQL-Anforderungsparameter an die execute -Methode des definierten Handlers übergeben wird.

Verwenden wir Ktor, um die GraphQL-Verarbeitung auf dem Endpunkt / graphql zu implementieren.

Routes.kt

//Angabe des Pfades
@Location("/graphql")
//Festlegen der Parameter, die in der Anforderung empfangen werden sollen
data class GraphQLRequest(val query: String = "", val operationName: String = "", val variables: Map<String, Any> = mapOf())

fun Application.routes() {
  
  val handler = GraphQLHandler()
  
  post<GraphQLRequest> {
      val request = call.receive<GraphQLRequest>()
      val query = request.query
      val operationName = request.operationName
      val variables = request.variables
      val context = ConcurrentHashMap<String, Any>()
      call.respond(
        //Führen Sie die GraphQL-Verarbeitung aus
        handler.execute(query, operationName, variables, context).toSpecification()
      )
  }
}

Wenn Sie jetzt den Server starten, kann der Endpunkt / graphql GraphQL-Anforderungen akzeptieren: smile:

Am Ende

Diesmal habe ich einen GraphQL-Server mit einer einfachen Datenstruktur implementiert. Der aktuelle Code wird bei Bedarf veröffentlicht.

Ich hoffe, es wird Ihnen helfen.

Recommended Posts

Versuchen Sie, einen GraphQL-Server mit grahpql-java-tools (+ kotlin) zu implementieren.
Versuchen Sie es mit libGDX
Versuchen Sie es mit Maven
Versuchen Sie es mit powermock-mockito2-2.0.2
Versuchen Sie es mit GraalVM
Versuchen Sie es mit jmockit 1.48
Versuchen Sie es mit SwiftLint
Versuchen Sie es mit Log4j 2.0
Versuchen Sie, das Eratostenes-Sieb mithilfe der Java-Standardbibliothek zu implementieren
Versuchen Sie es mit dem Axon Framework
Versuchen Sie es mit der Methode java.lang.Math
Versuchen Sie es mit der WhiteBox von PowerMock
Versuchen Sie es mit Talend Teil 2
Versuchen Sie es mit Talend Teil 1
Versuchen Sie es mit der F # -Liste
Versuchen Sie es mit der Methode each_with_index
Versuchen Sie es mit Spring JDBC
Versuchen Sie, mit Helidon einen WebAP-Server auf dem Mikro zu starten