title: how to do field level resolvers using graphql-kotlin with ktor framework
body: <p>how to do field level(non root) resolvers using gaphql-kotlin ? My code working <em>(date_range in API1Response is null as i dont know how to call my second resolver function getAPI2Response() when date_range exists in query)</em> when each query called separately as i have two suspend functions with concern query names, but i want to call two suspend functions with one query based on a field(non root) check in query. For example, in below shown code (service), if my query is "getAPI1Response" with fields "name","id","date_range", then graphql should call getAPI1Response and also getAPI2Response and map it to Data model "API1Response" so users can use single query instead firing two queries.</p>
  
  <p>Checked few samples from graphql-kotlin samples but all are referring to springboot which i am not using.</p>
  
  <pre><code>//DATA MODEL
  
  data class API1Response(
      val name: String?,
      val id: Int?,
      val date_range: API2Response?
  )
  
  data class API2Response(
      val max_date_range: ZonedDateTime?,
      val min_date_range: ZonedDateTime?
  )
  </code></pre>
  
  <pre><code>//SERVICE
  class Query() {
      private val httpClient = HttpClient()
  
  // when query name is "getAPI1Response", this function get triggers
      suspend fun getAPI1Response(): API1Response {
          // call API 1 and map response to API1Response model.
          return resp.content.toInputStream().use {
                      jackson.readValue(it, API1Response::class.java)
                  }
      }
  
  //// when query name is "getAPI2Response", this function get triggers
      suspend fun getAPI2Response(): API2Response {
          // call API 2 and map response to API2Response model.
          return resp.content.toInputStream().use {
                      jackson.readValue(it, API2Response::class.java)
                  }
      }
  }
  </code></pre>
  
  <pre><code>// GRAPHQL HANDLER
  
  package com.my.package.graphql
  
  import com.expedia.graphql.SchemaGeneratorConfig
  import com.expedia.graphql.TopLevelObject
  import com.expedia.graphql.hooks.SchemaGeneratorHooks
  import com.expedia.graphql.toSchema
  import com.my.package.errors.ErrorType
  import com.my.package.http.*
  import com.my.package.json.Jackson
  import graphql.ExecutionInput
  import graphql.GraphQL
  import graphql.language.StringValue
  import graphql.schema.*
  import graphql.schema.idl.SchemaPrinter
  import io.ktor.application.ApplicationCall
  import io.ktor.application.call
  import io.ktor.http.ContentType
  import io.ktor.http.HttpStatusCode
  import io.ktor.response.respondText
  import io.ktor.routing.Route
  import io.ktor.routing.get
  import io.ktor.routing.post
  import java.time.LocalDate
  import java.time.ZonedDateTime
  import java.time.format.DateTimeFormatter
  import kotlin.reflect.KClass
  import kotlin.reflect.KType
  
  class GraphQLHandler(
      mutations: List&lt;Any&gt;,
      queries: List&lt;Any&gt;
  ) : Handler {
      private val schema = toSchema(
          config = SchemaGeneratorConfig(
              supportedPackages = listOf("com.my.package"),
              hooks = CustomSchemaGeneratorHooks(),
              topLevelQueryName = "Query",
              topLevelMutationName = "Mutation"
          ),
          mutations = mutations.map { TopLevelObject(it) },
          queries = queries.map { TopLevelObject(it) }
      )
  
      private val graphQL = GraphQL.newGraphQL(schema).build()
  
      override val path = "/graphql"
  
      override val routes: Route.() -&gt; Unit = {
          post {
              postQuery(call)
          }
      }
  
      private suspend fun postQuery(call: ApplicationCall) {
          val reqBody = call.receiveJson(Map::class)
          val query = reqBody["query"] as? String
          if (query == null) {
              call.respondError(
                  HttpStatusCode.BadRequest,
                  ErrorType.bad_request,
                  "missing or invalid query field in json"
              )
              return
          }
  
          @Suppress("UNCHECKED_CAST")
          val variables = reqBody["variables"] as? Map&lt;String, Any&gt; ?: emptyMap()
  
          handleQuery(call, query, variables)
      }
  
  // main function which calls my concern suspend funciton mapping query name to function name
      private suspend fun handleQuery(call: ApplicationCall, query: String, variables: Map&lt;String, Any&gt;?) {
          val executionInput = ExecutionInput(query, null, call.request.authContext, null, variables)
          val result = graphQL.execute(executionInput).toSpecification()
          val statusCode = if (result.containsKey("errors")) HttpStatusCode.InternalServerError else HttpStatusCode.OK
          call.respondJson(statusCode, result, Jackson.all)
      }
  }
  
  class CustomSchemaGeneratorHooks : SchemaGeneratorHooks {
      override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier as? KClass&lt;*&gt;) {
          ZonedDateTime::class -&gt; graphqlZonedDateTimeType
          LocalDate::class -&gt; graphqlLocalDateType
          else -&gt; null
      }
  }
  
  val graphqlLocalDateType = GraphQLScalarType("LocalDate",
      " ISO date format without an offset, such as '2011-12-03' ",
      object : Coercing&lt;LocalDate, String&gt; {
          //override few functions here
      }
  )
  
  val graphqlZonedDateTimeType = GraphQLScalarType("ZonedDateTime",
      " ISO date-time format with an offset, such as '2011-12-03T10:15:30+01:00' ",
      object : Coercing&lt;ZonedDateTime, String&gt; {
          //override few functions here
      }
  )
  </code></pre>