Essayez d'implémenter un serveur GraphQL en utilisant grahpql-java-tools (+ kotlin)

Quel est cet article?

En utilisant Kotlin + graphql-java-tools, nous résumerons le processus d'implémentation du serveur GraphQL et de retour d'une réponse.

Y compris dans cet article --Construire un serveur GraphQL simple

Non inclus dans cet article --Contre-mesures pour le problème N + 1

À propos de graphql-java-tools

Il fournit la fonction de serveur GraphQL. Un outil appelé graphql-spring-boot utilise également graphql-java-tools en interne. Le serveur GraphQL lui-même ne fonctionne pas comme un serveur d'applications Web. Vous devez utiliser n'importe quel framework ou bibliothèque. Dans cet article, nous utiliserons Ktor.

Flux de mise en œuvre

  1. Définissez le schéma du fichier graphql
  2. Implémentez RootResolver
  3. Implémenter le résolveur
  4. Implémentez un gestionnaire pour gérer les requêtes GraphQL

À propos de la différence entre le résolveur de racine et le résolveur

graphql-java-tools a deux types de concepts Resolver. (Le résolveur est comme le contrôleur dans MVC)

Envisagez d'implémenter le schéma GraphQL suivant. Les données dans lesquelles le parent «Parent» a l'enfant «Child».

example.graphql


type Query {
  parent: Parent!
}

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

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

Lors du retour de données avec une telle relation Has A avec graphql-java-tools, il est nécessaire d'implémenter deux types, RootResolver et Resolver. Les rôles de chacun sont les suivants.

RootResolver: Implémente ce qui se passe lorsque le parent est référencé. Appelé avec une demande au point de terminaison comme déclencheur.

Resolver: Implémente le traitement lorsque Parent.Child est référencé. Il est appelé lorsque la classe parent enregistrée est appelée comme déclencheur.

C'est difficile à comprendre, alors je vais l'expliquer avec un exemple.

Supposons que le type défini dans ʻexmple.graphql` soit représenté par une classe Kotlin comme suit.


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

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

«Parent.child» n'existe pas dans la classe réelle. Pour renvoyer Parent.child comme réponse, vous devez utiliser Parent.childId pour associer Child. Pour ce faire, vous avez besoin de Resolver. Il peut recevoir l'objet Resolver lorsque la classe spécifiée est renvoyée en réponse. Par conséquent, vous pouvez faire référence à childId en recevant Parent avec ParentResolver, et vous pouvez obtenir le Child associé.

Résumé··· Traitement lorsque le point de terminaison défini dans Query est référencé -> RootResolver Que faire lorsqu'une propriété de type renvoyée par un point de terminaison est référencée -> Résolveur

1. Définissez le schéma

Voyons le déroulement de la mise en œuvre réelle du serveur.

Tout d'abord, définissons le schéma GraphQL. Cet article ne couvre pas la syntaxe de GraphQL, veuillez donc vous référer à un autre article pour la syntaxe.

Ici, nous allons implémenter un processus de requête simple. Créez un fichier appelé sample.graphql dans n'importe quel répertoire. Le serveur créé cette fois lira la définition de ce fichier.

J'ai défini un point de terminaison appelé samples pour renvoyer les données de type Sample sous forme de liste.

graphql/sample.graphql

type Query {
  samples: [Sample!]!
}

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

type User {
  id: ID
  sampleId: ID
}

La classe de données correspondante est également définie dans Kotlin.


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

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

2. Implémentez RootResolver

Nous allons implémenter RootResolver. Implémente ce qui se passe lorsque les échantillons de point de terminaison sont appelés. À l'origine, je pense que UseCase etc. sera appelé, mais cette fois je vais faire en sorte que la même liste soit renvoyée.

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)
    )
  }
}

Il y a deux points.

  1. Héritez de GraphQLQueryResolver. Cette fois, nous allons implémenter l'opération de Query, donc héritons de GraphQLQueryResolver. Hériter de GraphQLMutaionResolver lors de l'implémentation de l'opération de Mutation. Si les deux processus sont implémentés, un RootResolver peut hériter à la fois de GraphQLQueryResolver et de GraphQLMutaionResolver.

  2. Préparez une méthode portant le même nom que la requête définie dans GraphQL. Puisque graphql / sample.graphql définit un point de terminaison de requête appelé samples, RootReposolver doit également définir une méthode samples. A ce moment, si l'argument est spécifié côté GraphQL, il est nécessaire de recevoir un argument du type correspondant dans la méthode de RootReposolver.

3. Implémenter le résolveur

Nous allons implémenter SampleResolver. Implémente le traitement lorsque la propriété de Sample.user est appelée.

Ce SampleResolver est déclenché lorsque la classe Sample est renvoyée en tant que réponse. Comme d'habitude, UseCase n'est pas appelé et renvoie un objet avec un «id» correspondant à partir d'une liste fixe.

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 }
  }
  
}

Il y a deux points.

  1. Héritez de GraphQLResolver. Héritons de GraphQLResolver en spécifiant l'objet que le résolveur veut recevoir dans les génériques. (Dans cet exemple, héritez de GraphqlResolver <Sample>)

  2. Préparez une méthode avec le même nom que la propriété définie dans graphql Puisque nous implémenterons le traitement lorsque Sample.user sera référencé comme défini dans GraphQL, nous implémenterons la méthode ʻuser`.

4. Implémenter le gestionnaire

Enregistrez le fichier sample.graphql et le résolveur définis précédemment dans le gestionnaire. Cela permet au serveur GraphQL de traiter votre demande.

GraphqlHander.kt

class GraphQLHander {

  /**
  *Construisez le schéma GraphQL.
  *Enregistrez le schéma et le résolveur définis dans filePath.
  **/
  fun init(): GraphQL {
    val filePath = "graphql/sample.graphql" 
    val schema = SchemaParser.newParser()
      //Lire la définition GraphQL
      .file(file)
      //Chargez le résolveur et associez-le au point de terminaison GraphQL
      .resolvers(listOf(RootSampleResolver(), SampleResolver())) 
      .build()
      .makeExecutableSchema()
  }

  /**
  *Gère les requêtes GraphQL.
  **/
  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())
      )
  }
}

Le traitement GraphQL peut être effectué en passant le paramètre Requête GraphQL à la méthode ʻexecute` du gestionnaire défini.

Utilisons Ktor pour implémenter le traitement GraphQL sur le point de terminaison / graphql.

Routes.kt

//spécification du chemin
@Location("/graphql")
//Définition des paramètres à recevoir dans la demande
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(
        //Exécuter le traitement GraphQL
        handler.execute(query, operationName, variables, context).toSpecification()
      )
  }
}

Désormais, lorsque vous démarrez le serveur, le point de terminaison / graphql peut accepter les requêtes GraphQL: smile:

À la fin

Cette fois, j'ai implémenté un serveur GraphQL avec une structure de données simple. Le code réel sera affiché s'il y a une demande.

J'espère que cela vous aidera.

Recommended Posts

Essayez d'implémenter un serveur GraphQL en utilisant grahpql-java-tools (+ kotlin)
Essayez d'utiliser libGDX
Essayez d'utiliser Maven
Essayez d'utiliser powermock-mockito2-2.0.2
Essayez d'utiliser GraalVM
Essayez d'utiliser jmockit 1.48
Essayez d'utiliser SwiftLint
Essayez d'utiliser Log4j 2.0
Essayez d'implémenter le tamis Eratostenes en utilisant la bibliothèque standard de Java
Essayez d'utiliser le Framework Axon
Essayez d'utiliser la méthode java.lang.Math
Essayez d'utiliser la WhiteBox de PowerMock
Essayez d'utiliser Talend Part 2
Essayez d'utiliser Talend Part 1
Essayez d'utiliser la liste F #
Essayez d'utiliser la méthode each_with_index
Essayez d'utiliser Spring JDBC
Essayez de lancer un serveur WebAP sur le micro à l'aide d'Helidon