带有 Sangria 的 GraphQL Schema
GraphQL Schema with Sangria
我正在查看用于在 Scala 中编写 GraphQL 服务器代码的 Sangria 库。然而,奇怪的是,同一类型系统必须实现两次:(1) 作为 GraphQL 类型声明的一部分,以及 (2) 同样在服务器端,如 Scala 案例 类,伴随着 ObjectType, InterfaceType 等 vals.
在 Scala 中对类型系统进行硬编码尤其令人厌烦,因为我的目的是能够对任意形状的聚合进行 CRUD,其中每个形状都定义为一个 GraphQL 类型集合。例如,假设一个 Shape 类型的实例包含一个 GraphQL 文档作为一个字段;并且 Entity 类型的实例具有对其 Shape 的引用,并且还包含该 Shape 中定义的形状的 Json 对象。
case class Shape(id: String, name: String, doc: sangria.ast.Document)
case class Entity(id: String, name: String, shape: Shape, content: JsValue)
例如,如果形状文档是这样的:
type Person {
firstName: String!
lastName: String!
age: Int
}
那么实体中的 Json 内容可能是这样的:
{
"firstName": "John",
"lastName": "Smith",
"age": 30
}
(一个真实的例子,当然,也有嵌套类型等)
因此,我寻求能够定义实体类型的实例,其形状在其对应的形状中定义。我不想对相应的 sangria.schema.Schema 进行硬编码,而是想直接从形状文档中导出它。
是否有现成的方法可以从包含类型声明的 GraphQL 文档以编程方式生成 GraphQL 架构?
对于此类动态用例,sangria 提供了一种从 GraphQL IDL 构建模式的方法。以下是您可以如何做到这一点(我稍微简化了您的示例,但是当所有这些数据来自单独的 类 时,如 Shape
和 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)
为了定义 resolve
函数应该如何生成,您需要创建一个自定义架构构建器,就像这个一样,只需重写 resolveField
方法:
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
}
}
执行此示例时,您将看到以下内容 JSON result
:
{
"data": {
"people": [{
"firstName": "John",
"age": 30
}]
}
}
如果您想查看更复杂的示例,我建议您查看 GrapohQL Toolbox "proxy"。该项目更进了一步,甚至添加了自定义指令来控制解析函数的生成。代码可以在这里找到:
我正在查看用于在 Scala 中编写 GraphQL 服务器代码的 Sangria 库。然而,奇怪的是,同一类型系统必须实现两次:(1) 作为 GraphQL 类型声明的一部分,以及 (2) 同样在服务器端,如 Scala 案例 类,伴随着 ObjectType, InterfaceType 等 vals.
在 Scala 中对类型系统进行硬编码尤其令人厌烦,因为我的目的是能够对任意形状的聚合进行 CRUD,其中每个形状都定义为一个 GraphQL 类型集合。例如,假设一个 Shape 类型的实例包含一个 GraphQL 文档作为一个字段;并且 Entity 类型的实例具有对其 Shape 的引用,并且还包含该 Shape 中定义的形状的 Json 对象。
case class Shape(id: String, name: String, doc: sangria.ast.Document)
case class Entity(id: String, name: String, shape: Shape, content: JsValue)
例如,如果形状文档是这样的:
type Person {
firstName: String!
lastName: String!
age: Int
}
那么实体中的 Json 内容可能是这样的:
{
"firstName": "John",
"lastName": "Smith",
"age": 30
}
(一个真实的例子,当然,也有嵌套类型等)
因此,我寻求能够定义实体类型的实例,其形状在其对应的形状中定义。我不想对相应的 sangria.schema.Schema 进行硬编码,而是想直接从形状文档中导出它。
是否有现成的方法可以从包含类型声明的 GraphQL 文档以编程方式生成 GraphQL 架构?
对于此类动态用例,sangria 提供了一种从 GraphQL IDL 构建模式的方法。以下是您可以如何做到这一点(我稍微简化了您的示例,但是当所有这些数据来自单独的 类 时,如 Shape
和 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)
为了定义 resolve
函数应该如何生成,您需要创建一个自定义架构构建器,就像这个一样,只需重写 resolveField
方法:
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
}
}
执行此示例时,您将看到以下内容 JSON result
:
{
"data": {
"people": [{
"firstName": "John",
"age": 30
}]
}
}
如果您想查看更复杂的示例,我建议您查看 GrapohQL Toolbox "proxy"。该项目更进了一步,甚至添加了自定义指令来控制解析函数的生成。代码可以在这里找到: