Search code examples
scalagraphqlsangria

Sangria GraphQL: How to mix deferred fields, deriveObjectType, and case classes


I'm curious if it's possible to define a case class's field as deferred while still using the deriveObjectType macro to define everything else.

Here's an example. Dashboards contain a sequence of tabs:

case class Tab(id: Long, dashboardId: Long, name: String, order: Long)

case class Dashboard(id: Long, name: String, tabs: Seq[Tab])

I'm deferring resolution of the Dashboard.tabs field using a Fetcher, AND I'd like to continue using the deriveObjectType macro (if possible). So here's how I've defined my ObjectTypes:

val TabType = deriveObjectType[Unit, Dashboard]()

val DashboardType = deriveObjectType[Unit, Dashboard](
  AddFields(
    fields =
      Field(
        name = "tabs",
        fieldType = ListType(TabType),
        resolve = ctx => {
          TabsFetcher.fetcher.defer(ctx.value.id)
        }
      )
  )
)

But, when I run the code, I get the following error:

sangria.schema.NonUniqueFieldsError: All fields within 'Dashboard' type should have unique names! Non-unique fields: 'tabs'.

If I remove the tabs field from the Dashboard case class the error goes away, but I lose some of the benefit of using a case class (especially in unit tests). And if I avoid the use of the deriveObjectType macro (and define the Dashboard's ObjectType manually), then I lose the benefits of the macro (less boilerplate).

So, I'm curious if there's a better way, or another way, around this issue short of defining the DashboardType without the use of the macro or removing the tags field from the Dashboard case class.

(I was hoping that there might be some sort of @GraphQLDeferred annotation that I could apply to the Dashboard.tabs field or something similar???)


Solution

  • You almost got it right. You need use ReplaceField instead of AddFields. Alternatively you can ExcludeFields("tabs") and continue using AddFields.