ghostdogpr / caliban

Functional GraphQL library for Scala
https://ghostdogpr.github.io/caliban/
Apache License 2.0
943 stars 248 forks source link

Interface not working with union types #2397

Closed develeon closed 3 days ago

develeon commented 1 week ago

When rendering GraphQL from code it seems interfaces are not created if implemented by union types.

A common usage for this is when using typed errors on mutation and a collective interface for all error types. Such as explained in the Logrocket example.

Expected would have been that interface error would have been created and error types would have implemented it.

image

The following branch was setup with a test for expected result.

Believe that the issue relates to union not having implements so that the interface does not get picket up when parsing through the type definitions.

ghostdogpr commented 3 days ago

So, the way that Caliban derivation works is that it extracts the GraphQL types from the types that are used in fields or arguments of the schema. However here Error is never returned, types just implement it. If you try to add a new field test: Error in Query, you will see interface Error appear: https://scastie.scala-lang.org/XTM8qM3IQOiy2KnMKn7H2g

There is a workaround if you don't want to use Error directly in your schema: use withAdditionalTypes to let Caliban know to add the interface to the schema. There is an example here: https://github.com/ghostdogpr/caliban/blob/4432d11ec86c5c354eda32e499ddf3826881dab4/core/src/test/scala/caliban/schema/SchemaSpec.scala#L226

Actually I just found out that it was documented here: https://ghostdogpr.github.io/caliban/faq/#my-interface-is-missing-from-the-schema

develeon commented 3 days ago

Thanks! This workaround does the trick. Think reason the interface did not get picket up was due to union types not having implements, but anyway this works. Since we use derives it turned out something like this:

type ConnectionEnv = Tracing & java.sql.Connection & Auth
object EnvSchema extends caliban.schema.SchemaDerivation[ConnectionEnv]

@GQLInterface
sealed trait Error extends scala.Product with scala.Serializable derives EnvSchema.SemiAuto {
  def message: String
}

object Error {
  given errorSchema Schema[ConnectionEnv, Error] = Schema.gen[ConnectionEnv, Error]
  val errorType: __Type = errorSchema.toType_()
}