【问题标题】:How to create generics with the schema language?如何使用模式语言创建泛型?
【发布时间】:2018-01-13 05:35:12
【问题描述】:

使用 facebook 的参考库,我找到了一种破解泛型类型的方法,如下所示:

type PagedResource<Query, Item> = (pagedQuery: PagedQuery<Query>) => PagedResponse<Item>
​
interface PagedQuery<Query> {
  query: Query;
  take: number;
  skip: number;
}
​
interface PagedResponse<Item> {
  items: Array<Item>; 
  total: number;
}

function pagedResource({type, resolve, args}) {
  return {
    type: pagedType(type),
    args: Object.assign(args, {
      page: { type: new GraphQLNonNull(pageQueryType()) }
    }),
    resolve
  };
  function pageQueryType() {
    return new GraphQLInputObjectType({
      name: 'PageQuery',
      fields: {
        skip: { type: new GraphQLNonNull(GraphQLInt) },
        take: { type: new GraphQLNonNull(GraphQLInt) }
      }
    });
  }
  function pagedType(type) {
    return new GraphQLObjectType({
      name: 'Paged' + type.toString(),
      fields: {
        items: { type: new GraphQLNonNull(new GraphQLList(type)) },
        total: { type: new GraphQLNonNull(GraphQLInt) }
      }
    });
  }
}

但我喜欢使用 Apollo Server 以声明方式创建模式的方式。所以问题是,你们如何使用模式语言创建类似泛型的类型?

【问题讨论】:

    标签: graphql graphql-js apollo-server


    【解决方案1】:

    您可以创建一个接口或联合来实现类似的结果。我认为this article 在解释如何正确实现接口和联合方面做得很好。您的架构将如下所示:

    type Query {
      pagedQuery(page: PageInput!): PagedResult
    }
    
    input PageInput {
      skip: Int!
      take: Int!
    }
    
    type PagedResult {
      items: [Pageable!]!
      total: Int
    }
    
    # Regular type definitions for Bar, Foo, Baz types...
    
    union Pageable = Bar | Foo | Baz
    

    您还需要为联合定义一个 resolveType 方法。对于graphql-tools,这是通过解析器完成的:

    const resolvers = {
      Query: { ... },
      Pageable {
        __resolveType: (obj) => {
          // resolve logic here, needs to return a string specifying type
          // i.e. if (obj.__typename == 'Foo') return 'Foo'
        }
      }
    }
    

    __resolveType 将要解析的业务对象作为其第一个参数(通常是您提供给 GraphQL 解析的原始数据库结果)。您需要在此处应用一些逻辑来找出所有不同的 Pageable 类型,即我们正在处理的类型。对于大多数 ORM,您只需将某种 typename 字段添加到您正在使用的模型实例中,然后让 resolveType 返回它。

    编辑: 正如您所指出的,这种方法的缺点是 items 中返回的类型不再对客户端透明 - 客户端必须知道返回的类型和在像... on Foo 这样的内联片段中指定items 的字段。当然,您的客户仍然需要对返回的类型有所了解,否则他们将不知道要请求哪些字段。

    我想在以声明方式生成架构时,不可能按照您想要的方式创建泛型。要让您的架构以与当前相同的方式工作,您必须硬着头皮在定义 Foo 时定义 PagedFoo,在定义 Bar 时定义 PagedBar 等等。

    我能想到的唯一其他选择是将这两种方法结合起来。以编程方式创建您的“基础”架构。您只需要使用 pagedResource 函数在根查询下定义分页查询。然后,您可以使用graphql/utilities 中的printSchema 将其转换为可以与其余类型定义连接的字符串。在您的类型定义中,您可以使用 extend 关键字来构建已在基本架构中声明的任何类型,如下所示:

    extend Query {
      nonPaginatedQuery: Result
    }
    

    如果您走这条路,您可以跳过将resolve 函数传递给pagedResource,或在您以编程方式定义的类型上定义任何解析器,而只需使用您通常传递给buildExecutableSchema 的解析器对象。

    【讨论】:

    • 这不是说客户端需要弄清楚返回的是什么类型吗?
    • 是的,客户端必须使用内联片段。不确定是否有一种好方法可以在不以编程方式生成模式的情况下完成您想要完成的工作。请参阅我的编辑以了解其他想法。
    • 感谢您的努力
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-27
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 2019-01-09
    相关资源
    最近更新 更多