I have the following case classes :
case class OrganizationId(value: Long) extends AnyVal
case class Organization(id: OrganizationId, name: String, iban: Option[String], bic: Option[String], updatedAt: LocalDateTime, insertedAt: LocalDateTime)
case class EventId(value: Long) extends AnyVal
case class Event(
id: EventId, organizationId: OrganizationId, name: String, description: Option[String], updatedAt: LocalDateTime, insertedAt: LocalDateTime
)
They represent a simple schema where an Organization
can have multiple Event
.
Basically I'm trying to define two graphql queries : one that list all organizations with its nested events and the second query is the other way : list all events with its corresponding organization. In both cases I'd like to avoid N+1 queries. I think it's possible for both queries to be run in 2 SQL queries each.
To solve this issue I had a look at the high level Fetch API and specifically the Fetcher.rel
part. If I understand correctly I'll need to define a relation like this : val byOrganization = Relation[Event, OrganizationId]("byOrganization", e => Seq(e.organizationId))
and then a Fetcher.rel
, but I can't make it work.
Here is my full code for my schema definition :
class GraphqlSchema {
val byOrganization = Relation[Event, OrganizationId]("byOrganization", e => Seq(e.organizationId))
val organizationsFetcher = Fetcher(
(context: GraphqlContext, ids: Seq[OrganizationId]) => {
context.organizations.all(ids)
}
)(HasId(_.id))
val eventsFetcher = Fetcher.rel[GraphqlContext, Event, Organization, EventId](
(context: GraphqlContext, ids: Seq[EventId]) => {
context.events.all(ids)
},
(context: GraphqlContext, ids: RelationIds[Event]) => {
???
}
)(HasId(_.id))
val deferredResolver = DeferredResolver.fetchers(organizationsFetcher, eventsFetcher)
implicit val OrganizationIdType = CustomScalarTypes.organiaztionIdType
implicit val EventIdType = CustomScalarTypes.eventIdType
implicit val UuidType = CustomScalarTypes.uuidType
implicit val LocalDateTimeType = CustomScalarTypes.localDateTimeType
implicit val UserType = deriveObjectType[GraphqlContext, User]()
implicit val OrganizationType: ObjectType[GraphqlContext, Organization] = deriveObjectType[GraphqlContext, Organization](
AddFields(
Field("events", ListType(EventType), resolve = (ctx) => {
eventsFetcher.deferRelSeq(byOrganization, ctx.value.id)
})
)
)
implicit val EventType: ObjectType[GraphqlContext, Event] = deriveObjectType[GraphqlContext, Event](
AddFields(
Field("organization", OptionType(OrganizationType), resolve = (ctx) => {
organizationsFetcher.deferOpt(ctx.value.organizationId)
})
)
)
val QueryType = deriveContextObjectType[GraphqlContext, Query, Unit](_.query)
val MutationType = deriveContextObjectType[GraphqlContext, Mutation, Unit](_.mutation)
val schema = Schema(QueryType, Some(MutationType))
}
It doesn't compile, I get this error: ound :
[error] /Users/matthieu/Code/runevent/runevent/scala/api/src/main/scala/com/runevent/api/graphql/GraphqlSchema.scala:39:35: type mismatch;
[error] found : sangria.execution.deferred.Relation[com.runevent.database.models.Event,com.runevent.database.models.Event,com.runevent.database.models.OrganizationId]
[error] required: sangria.execution.deferred.Relation[com.runevent.database.models.Event,com.runevent.database.models.Organization,?]
[error] eventsFetcher.deferRelSeq(byOrganization, ctx.value.id)
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
Could you help me find out what I'm missing?
I believe eventsFetcher
should be defined like this (so both Res
and RelRes
type parameters are of the same type Event
):
val eventsFetcher = Fetcher.rel[GraphqlContext, Event, Event, EventId](
(context: GraphqlContext, ids: Seq[EventId]) => {
context.events.all(ids)
},
(context: GraphqlContext, ids: RelationIds[Event]) => {
context.events.loadByOrganizationIds(ids(byOrganization))
}
)(HasId(_.id))