by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
Peter Gerhard
@peter-gerhard

:wave: Hi, Anyone knows how to properly embed objects in graphQL schema? I did not find anything in the documentation:
I have the following use-case:

case class Outer(a: String, embedded: Embedded)
case class Embedded(c: String, d: String)

What I would like to achieve is that the fields c and d are accessible in the api directly from the outer object.

The only way I currently see to achieve this is to manually add these fields in the OuterType definition:

implicit val EmbeddedType = deriveObjectType[MyCtx, Embedded]()
implicit val OuterType = deriveObjectType[MyCtx, Outer](
  AddFields(
    Field("c", StringType, resolve = _.embedded.c),
    Field("d", StringType, resolve = _.embedded.d)))

The problem here is that this is not forward compatible if we add new fields to Embedded in the future.

kamisama-rney
@kamisama-rney

@mesutyigit which version of Scala are you using? I just came up against this using Scala 2.13.3 and ended up converting my schema definitions to explicit definitions via ObjectType

 ObjectType(
      "Application",
      () =>
        fields[MasterRepo[F], Application](
          Field(

to get my code compiling again. We were fine until I started adding in the documentation via @GraphQLDescription()

Mesut Yiğit
@mesutyigit
I see this error both of scala 2.13.2 and 2.13.3
kamisama-rney
@kamisama-rney
That's expected given those are only 1 minor release apart. I was curious if this was also experienced in 2.12.10 or other "current" versions of Scala 2.12. I didn't rollback to 2.12 since I have a bunch of Java conversions in my schema case classes since I'm ingesting data from a common Avro library and the package for JavaConversions changed between 2.12 and 2.13
Erik
@erikdstock
how do folks generally organize their schema files? Especially given that our are all vals they don't lend themselves to a package structure
The only example I can find is https://github.com/sangria-graphql/sangria-relay-playground/blob/master/app/models/SchemaDefinition.scala - which for a very small schema is a very large file
kamisama-rney
@kamisama-rney

@erikdstock I'm in the process of designing a new schema right now that's quite large. I've created a model package with several sub-packages for each collection of Type objects. Since we're using http4s, functional-streams for scala, and cats.effect for our code we've implemented everything as object with the apply method constructing the type

object ApplicationType {

  def apply[F[_]: Effect]: ObjectType[MasterRepo[F], Application] = {

    ObjectType(
      "Application",
      () =>
        fields[MasterRepo[F], Application](
          Field(
            "appId",
            StringType,
            resolve = _.value.appId
           )
        )
    )
}

Most of the types are in their own files if they're only nested 1 level from the root schema item Application. For items I'm nesting down 2-3 levels and aren't shared with other types I've included them as implicit types at the top of the apply method.

Hope that helps

Erik
@erikdstock
Interesting, thanks.
kamisama-rney
@kamisama-rney
We also started with a separate schema JAR and used deriveObjectType[MasterRepo[F], Application] but once I started adding documentation to the classes with the Attributes a bug in Scala 2.13 was exposed in compile time reflection. Thus I went back to the old ObjectType approach.
Erik
@erikdstock
Alright, i've been wrestling with this a bit today and have an example here that works, but i'm curious on others' thoughts. In this case we have a Money case class that has been created specifically to define presentational logic for our money numbers, and the GraphQL MoneyType exists in the companion object. In other cases, we might have for example an object User might include a UserType even though the user model exists outside our graphql package. Does this make sense?
https://gist.github.com/erikdstock/09dc318dfea84f5f4f307fc2a487aecd
Apologies that it's a bit verbose, it's one of our simpler bits of legacy code - you can mostly ignore the case class implementation to be honest.
Erik
@erikdstock
Minimal Example:
case class Foo(bar: Bar)

object Foo {
  lazy val FooType = deriveObjectType[Unit, Foo]
}
...
package object models { val FooType = Foo.FooType }
Erik
@erikdstock
This message was deleted
Erik
@erikdstock
Hi folks, I'm still having a hard time grokking when it is appropriate or necessary to use implicit vals the fieldsFn argument and so forth. It seems like no matter what I end up with a no implicit arguments found of type ValidOutType[...] on one specific enum type and some compile-time errors in at least one other case where it looks like i have defined the implicit val one line above its use - with no negative feedback from intellij.
One source of the complexity here seems to me that I am trying to pull these into separate traits [they have to be traits because of an implicit val ec: ExecutionContextExecutor], so I cannot easily mix them into each other
Erik
@erikdstock
Part of my confusion is with the docs and part is with the code i'm working with - being unsure if derive_Type is failing somewhere that I can't see due to macros, frequent use of both def & implicit val UserType: ObjectType[RequestServices, UserId]. In the validOutType case above that is also referring to a deriveEnumType (also defined in the same file) whose base trait is implemented entirely by case objects... Though i have to say, at this point this all is starting to feel like noisy word salad
kamisama-rney
@kamisama-rney
@erikdstock I suspect you're having issues with the JSON serialization. Which I admit the documentation doesn't give enough information on enums
@erikdstock Not sure which JSON parser you're I recommend you look into enumeratum which I used to get my Enum working with Circe
import enumeratum._
import sangria.macros.derive._
import sangria.schema._

sealed trait Platform extends EnumEntry
case object Platform extends CirceEnum[Platform] with Enum[Platform] {
  case object Android extends Platform
  case object iOS extends Platform

  val values: IndexedSeq[Platform] = findValues
}
And then include the correct marshalling package. Here's the ones I used for Circe
import io.circe.generic.auto._
import sangria.marshalling.FromInput
import sangria.marshalling.circe._
import sangria.schema._
import sangria.util.tag.@@
Enumeratum includes packages for all the common JSON parsers: Jackson, json4s, Argonaut...
Erik
@erikdstock
For now at least, we are using spray with a self: SprayJsonConversions => in every trait
I'm sorry I feel like I am running this chat with a lot of very basic questions
kamisama-rney
@kamisama-rney
Everyone has to learn sometime. No worries.
Erik
@erikdstock
I'm oscillating back and forth between thinking I have found something easier to read and understand and thinking this is getting very galaxy brained
kamisama-rney
@kamisama-rney
If you're planning to go forward with Spray you'll need to ensure the marshalling functions are declared implicit and visible with the marshaller
Sangria appears to support Circe's auto derivation functions for marshalling
And just a FYI: I just figured out how to handle the Enum marshalling last Thursday ;)
Erik
@erikdstock
Right. It's definitely working as-is so I am hestitant to make any big changes
but also being driven mad by these interdependent types, the overloaded method signatures and our existing mix of usages - an unimplemented def here, an implicit val there
Erik
@erikdstock
Are there any known, relatively large open-source sangria schemas out there? Things that go beyond conference talk demo projects?
Erik
@erikdstock
Another thing that would be helpful is a concise explanation of when lazy/implicits are necessary, when fields need to be defined as a function, and best practices for breaking up schema definition across multiple files

From the docs:

In order to discover other GraphQL types, the macros use implicits. So if you derive interdependent types, make sure to make them implicitly available in the scope.

Is this in reference to something like a deriveObjectType[RequestService, HasBInside] where, due to the use of the derive macro, there is no reference to the B field in the file?

Erik
@erikdstock

Another case here about circular object types:

In some cases you need to define a GraphQL schema that contains recursive types or has circular references in the object graph. Sangria supports such schemas by allowing you to provide a no-arg function that creates ObjectType fields instead of an eager list of fields. ... In most cases you also need to define (at least one of) these types with lazy val.

Is there some general scala background knowledge here that would hint at why this is necessary? Perhaps something related to forward referencing?

Samuel
@DevFlex
anyone on here have experience with sangria-akka-streams? I cannot seem to get it to work. For that matter I was trying to import sangria.streaming.akkaStreams but akkaStreams implementation isn't even recognized. Is this still supported?
kamisama-rney
@kamisama-rney
@erikdstock lazy and implicit declarations are common Scala. A lazy variable declaration is one that isn't initialized until it's accessed the first time during runtime. This gets around the compiler errors when a variable declaration isn't found. That's why they are recommending you use lazy on at least one variable when you have a circular reference.
kamisama-rney
@kamisama-rney

@erikdstock As for implicit this declares something that is searchable within package scopes by the compiler. If you're using the deriveObjectType[Context, Object] that's using compiler-time reflection. So if you have nested objects within a parent object you should have code that declares those types above and as implicit

object ParentType {
  def apply[F[_]: Effect]: ObjectType[MasterRepo[F], Parent] = {

    implicit val entryType: ObjectType[MasterRepo[F], Entry] = deriveObjectType[MasterRepo[F], Entry]
    implicit val itemType: ObjectType[MasterRepo[F], Item] = deriveObjectType[MasterRepo[F], Item]

    ObjectType(
      "Parent",
      () =>
        fields[MasterRepo[F], Parent](
          Field(
            "entries",
            ListType(itemType),
            resolve = _.value.entries
          )
        )
    )
  }
}

The implicit items at the top are located by the compiler when the Parent object is realized

Erik
@erikdstock

Thanks @kamisama-rney - i think I understand that much, with the exception of how the implicit keyword actually works. Example: a lazy val is a different object than a val, but implicit is just a flag. Minimal example:

trait CartTypes {
  val itemType: ObjectType[RequestServices, Item]
  lazy val cartType: ObjectType[Unit, Cart] = ObjectType[RequestServices, Cart](
  /* ... */
    fields = Field(
      "items",
      ListType(ItemType),
      resolve = c => c.ctx.getItems(c.value.id)
    )
  )
}

trait ItemTypes {
  implicit val cartType: ObjectType[RequestServices, ShoppingCart]
  lazy val itemType =  deriveObjectType[RequestServices, Item]()
}

case class Schema()(implicit val ec: ExecutionContextExecutor) extends CartTypes with ItemTypes {
  val QueryType = ObjectType(
    "Query",
    fields[RequestServices, Unit]( /* the types are used */ )
  )
}

My impression from the above is that

  • For CartTypes and ItemTypes, the lazy vals are due to interdependence on each other
  • val itemType: ObjectType[RequestServices, Item] means the member is necessary to be implemented in the concrete Schema implementation (straightforward)
  • implicit val cartType: ObjectType[RequestServices, ShoppingCart]does the same but adds an additional flag to the compiler to make this val available within the deriveObjectType macro. This is because we don't refer to it directly as with an ObjectType literal. The ItemTypes is allowed to make its implemented cartType implicit.

Is this all correct? I think part of my confusion was on lazy vs implicit and when the name bound to the variable needs to match (vs just the type). I realize some of this is just scala basics :/

Erik
@erikdstock
I guess maybe there is also an implicit val ec: ExecutionContextExecutor in the CartTypes(in my actual use case due to the our RequestServices implementation)
kamisama-rney
@kamisama-rney
Here's a nice series on Scala implicits - http://baddotrobot.com/blog/2015/07/03/scala-implicit-parameters/
Erik
@erikdstock
yes, i have read many articles on scala implicits but have a lot of holes in my understanding still
is my example above about when lazy vals and implicits are necessary broadly correct?
Jakub Kozłowski
@kubukoz
Hi, is it possible to generate object types for functions in a trait that require some context values? Example coming...
final case class Thing(name: String)
implicit val objectTypeThing = deriveObjectType[SecureContext,Thing]()

trait MyMutationWithContext {
  @GraphQLField
  def createThing(
    // ctx: User,
      name: String
  ): Future[Option[Thing]]
}
val myMutationType =
  deriveContextObjectType[SecureContext, MyMutationWithContext, Unit](_ => (??? : MyMutationWithContext))
this part compiles, but it stops compiling when I uncomment User - I'm new to this and don't really see a way to get that to work (I tried all the derivation methods)
Having SecureContext instead of User in there would be fine too
or should I just give up and write the field for that method manually?
Jakub Kozłowski
@kubukoz
I guess I'm looking for something that'll give me an InputType that takes something from the context...
hmm, maybe I don't need it after all - my MyMutationWithContext will be a field of the context already, which implies I can pass a user (which is a field in the context) at construction time
Rex Sheridan
@rex-sheridan
Looks like https://sangria-graphql.org/ is down
kamisama-rney
@kamisama-rney
Looks like the domain name expired July 28th, 2020
Domain name: sangria-graphql.org
Registry Domain ID: D176971954-LROR
Registrar WHOIS Server: whois.namecheap.com
Registrar URL: http://www.namecheap.com
Updated Date: 2018-09-25T18:52:13.00Z
Creation Date: 2015-07-28T21:48:18.00Z
Registrar Registration Expiration Date: 2020-07-28T21:48:18.00Z