0
votes

I'm working through a slight variation of Sangria's Getting Started, using Akka HTTP. I'm attempting to use json4s-jackson as the serializaltion lib, but am running in to some trouble getting the response I want.

Specifically, the serialized response I get is the JSON version of the (StatusCode, Node) tuple:

{
    "_1": {
        "allowsEntity": true, 
        "defaultMessage": "OK", 
        "intValue": 200, 
        "reason": "OK"
    }, 
    "_2": {
        "data": {
            "foo": {
                "id": "1", 
                "name": "Foo"
            }
        }
    }
}

The data portion is correct, but obviously I just want that and not the first element of the serialized tuple.

I'm using akka-http-json4s, so my trait with route looks like:

case class GraphQlData(query: String, operation: Option[String])

trait FooController {
  import de.heikoseeberger.akkahttpjson4s.Json4sSupport._

  implicit val serialization = jackson.Serialization
  implicit val formats = DefaultFormats

  val fooRoutes = post {
    entity(as[GraphQlData]) { data =>
      QueryParser.parse(data.query) match {

        // query parsed successfully, time to execute it!
        case Success(queryAst) =>
          complete {
            Executor
              .execute(
                SchemaDefinition.FooSchema,
                queryAst,
                new FooService,
                operationName = data.operation
              )
              .map(OK -> _)
              .recover {
                case error: QueryAnalysisError => BadRequest -> error.resolveError
                case error: ErrorWithResolver  => InternalServerError -> error.resolveError
              }
          }

        // can't parse GraphQL query, return error
        case Failure(error) =>
          complete(BadRequest -> error.getMessage)
      }
    }
  }

  implicit def executionContext: ExecutionContext
}

For the life of me I can't figure out what's wrong. I've been looking at sangria-akka-http-example but it seems to be exactly the same, with the exception of using spray-json instead of json4s.

Ideas? Thanks!

2

2 Answers

0
votes

Ah, figured it out. I neglected to add

import sangria.marshalling.json4s.jackson._

to the trait defining the route. Adding it does the trick.

0
votes

Just wanted to provide a quick update to this answer for the full GraphQLRequest. Now the variables are included in the request.

import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s._
import org.json4s.JsonAST.JObject
import sangria.marshalling.json4s.jackson._

case class GQLRequest(query: String, operationName: Option[String], variables: JObject)

trait SomeJsonSupport extends Json4sSupport {
    implicit val serialization = jackson.Serialization
    implicit val formats = DefaultFormats

}

trait GraphQLResource extends SomeJsonSupport{
  implicit val timeout:Timeout
  implicit val system:ActorSystem
  import system.dispatcher

  def graphqlRoute: Route =
    (post & path("graphql")) {
      entity(as[GQLRequest]) { requestJson =>
        println(s"This is the requestJson = $requestJson")
        graphQLEndpoint(requestJson)
      }
    } ~
      get {
        println(s"This is working")
        getFromResource("graphiql.html")
      }


  def graphQLEndpoint(requestJson: GQLRequest): Route = {
    val route = QueryParser.parse(requestJson.query) match {
      case Success(query) =>
        println(s"This is the query $query")


        val vars = requestJson.variables match {
          case jObj:JObject => jObj
          case _ => JObject(List.empty)
        }

        val futureJValue = Executor.execute(clientSchema,
          query,
          NclhGqlRequest(this),
          operationName = requestJson.operationName,
          variables = vars)

        val futureTupleStatusCodeJValue = futureJValue.map(OK -> _).recover {
          case error: QueryAnalysisError => BadRequest -> error.resolveError
          case error: ErrorWithResolver => InternalServerError -> error.resolveError
        }
        complete(futureTupleStatusCodeJValue)

      case Failure(error) =>
        complete(BadRequest, error.getMessage)
    }
    route

  }