覆盖 "root" GraphQL Union/Interface 类型的对象值

Overriding "root" object value of GraphQL Union/Interface type

我有一个委托给内部 gRPC 服务的 Apollo GraphQL 服务。此服务有一个端点,其中 returns 包含 oneof 的消息,我将其映射到 GraphQL 中的 Union

这很简单,但是在实施解析器时涉及相当程度的样板文件。假设我有以下 protobuf 消息定义:

message MyUnionMessage {
  oneof value {
    UnionType1 type1 = 1;
    UnionType1 type2 = 3;
    UnionType1 type3 = 4;
  }
}
message UnionType1 {<type 1 props>}
message UnionType2 {<type 2 props>}
message UnionType3 {<type 3 props>}

我对应的 GraphQL 架构如下所示:

union MyUnionType = UnionType1 | UnionType2 | UnionType3
type UnionType1 {<type 1 props>}
type UnionType1 {<type 2 props>}
type UnionType1 {<type 3 props>}

在 gRPC 的 javascript 绑定中,一个 MyUnionMessage 对象将有两个属性:value 是一个指示包含哪种类型的值的字符串,以及 属性 以类型命名。因此,例如,如果我有一个包含 UnionType2MyUnionMessage,该对象将如下所示:

{
  value: 'type2',
  type2: {...}
}

这很适合实现 __resolveType,因为我可以对 value 中的值做一个简单的 switch,但我必须为 编写一个解析器]all 所有具体类型的字段。

我正在寻找的是能够做到这样的事情:

resolvers = {
  MyUnionType: {
    __resolveType(obj) {
      switch(obj.value) {
      case 'type1': return 'UnionType1';
      case 'type2': return 'UnionType2';
      case 'type3': return 'UnionType3';
      default: return null;
    },
    __resolveValue(obj) {
      return obj[obj.value];
    },
  },
};

基本上,我想在通用联合(或接口)类型级别编写一个 "resolver",在将对象传递给具体解析器之前对其进行转换。

这样的事情可能吗?

我敢打赌,这种情况通常可以通过在数据达到 __resolveType 逻辑之前转换数据来解决。例如,假设您有一个 Query 字段 return 编辑了 MyUnionType 的列表。该字段的解析器可能类似于:

function resolve (arr) {
  return arr.map(obj => {
    return {
      ...obj[obj.value]
      type: obj.value // or whatever field name that won't cause a collision
    }
  })
}

然后在 __resolveTypetypeswitch 就可以了。当然,这意味着如果您有多个 return 和 MyUnionType 的字段,您需要将该逻辑提取到每个解析器都可以使用的效用函数中。

我认为没有真正的方法可以用现有的 API 来完成您想要做的事情。当然,您可以这样做:

const getUnionType(obj) {
  switch(obj.value) {
    case 'type1': return 'UnionType1';
    case 'type2': return 'UnionType2';
    case 'type3': return 'UnionType3';
    default: {
      throw new Error(`Unrecognized type ${obj.value}`)
    }
  }
}

const resolvers = {
  MyUnionType: {
    __resolveType(obj) {
      const type = getUnionType(obj)
      Object.assign(obj, obj[obj.value])
      return type
    },
  },
};

这行得通,但请记住它有点脆弱,因为它假设 resolveType 将始终获得与 resolve 函数相同的根值,假设将来可能会发生变化。