scalagraphqlsangria

GraphQL pass @include logic to the repository call


I have a query

{
    "query": "query($withComments: Boolean!) {feed(id: 2) {name comments @include(if: $withComments) {title text}}}",
    "vars": {"withComments": true}
}

Based on the withComments variable I can grab the feed with or without comments. It works. But it seems that Sangria has to get the feed with the comments in any case (what is the performance issue for me) even if I do not need them and passed withComments with the false value:

val QueryType = ObjectType(
    "Query",
    fields[GraphQLContext, Unit](
      Field("feed", OptionType(FeedType),
        arguments = Argument("id", IntType) :: Nil,
        resolve = c => c.ctx.feedRepository.get(c.arg[Int]("id")))))

What is the proper way to include/exclude inherit lists (say relations) in an Object and do not select all the data from the repository if I do not @include, make an repository aware of it?

If solution is to make two queries feed and feedWithComments I can not see any flexibility with the @include.


Solution

  • Since your data access object (resository) and the GraphQL schema are decoupled from each other, you need to explicitly propagate information about the inclusion/exclusion of specific fields.

    There is a number of ways to approach this, but I think the most simple one is to use projections. In your case it might look like this:

    val IdArg = Argument("id", IntType)
    
    val QueryType = ObjectType(
      "Query",
      fields[GraphQLContext, Unit](
        Field("feed", OptionType(FeedType),
          arguments = IdArg :: Nil,
          resolve = Projector(1, (c, projection) ⇒
            if (projection.exists(_.name == "comments"))
              c.ctx.feedRepository.getWithComments(c arg IdArg)
            else
              c.ctx.feedRepository.getWithoutComments(c arg IdArg)))))
    

    With Projector I ask the library to get me 1 level deep projection of nested fields (just the field names). Then based on this info I can fetch the data in different ways (with or without comments)