GraphQL Schema with Sangria

I am looking at the Sangria library for encoding GraphQL server in Scala. However, it seems strange that a system of the same type should be implemented twice: (1) as part of GraphQL type declarations and (2) also on the server side, as Scala class classes, with an accompanying ObjectType, InterfaceType, etc. vals.

Hard coding the type system in Scala is especially tedious, since my goal is to be able to CRUD aggregates of arbitrary shape, where each form is defined as a collection of GraphQL types. For example, say an instance of the Shape type contains a GraphQL document as a field; and an instance of type Entity has a link to its form and also contains a Json object of the form defined in that form.

case class Shape(id: String, name: String, doc: sangria.ast.Document) case class Entity(id: String, name: String, shape: Shape, content: JsValue) 

For example, if the form document looks something like this:

 type Person { firstName: String! lastName: String! age: Int } 

then the content of Json in essence could be something like this:

 { "firstName": "John", "lastName": "Smith", "age": 30 } 

(a real example, of course, would also have nested types, etc.)

Thus, I am trying to define instances of type Entity whose form is defined in the appropriate form. I do NOT want to hardcode the corresponding sangria.schema.Schema, but I want to get it directly from the form document.

Is there a ready-made way to generate a GraphQL schema programmatically from a GraphQL document containing type declarations?

+5
source share
1 answer

For such dynamic use cases, sangria provides a way to build a schema from IDL GraphQL. Here's how you can do it (I simplified your example a bit, but the same thing can be done when all this data comes from separate classes such as Shape and Entity ):

 import sangria.ast._ import sangria.schema._ import sangria.macros._ import sangria.marshalling.sprayJson._ import sangria.execution.Executor import scala.concurrent.ExecutionContext.Implicits.global import spray.json._ val schemaAst = gql""" type Person { firstName: String! lastName: String! age: Int } type Query { people: [Person!] } """ val schema = Schema.buildFromAst(schemaAst, builder) val query = gql""" { people { firstName age } } """ val data = """ { "people": [{ "firstName": "John", "lastName": "Smith", "age": 30 }] } """.parseJson val result = Executor.execute(schema, query, data) 

To determine how resolve functions should be created, you need to create your own schema builder, like this one, and simply override the resolveField method:

 val builder = new DefaultAstSchemaBuilder[JsValue] { override def resolveField(typeDefinition: TypeDefinition, definition: FieldDefinition) = typeDefinition.name match { case "Query" ⇒ c ⇒ c.ctx.asJsObject.fields get c.field.name map fromJson case _ ⇒ c ⇒ fromJson(c.value.asInstanceOf[JsObject].fields(c.field.name)) } def fromJson(v: JsValue) = v match { case JsArray(l) ⇒ l case JsString(s) ⇒ s case JsNumber(n) ⇒ n.intValue() case other ⇒ other } } 

When you run this example, you will see the following JSON result :

 { "data": { "people": [{ "firstName": "John", "age": 30 }] } } 

If you want to see a more complex example, I would recommend you check out the GrapohQL Toolbox "proxy" . This project takes it one step further and even adds custom directives to control the generation of the permission function. The code can be found here:

https://github.com/OlegIlyenko/graphql-toolbox

+3
source

Source: https://habr.com/ru/post/1267248/


All Articles