构建 GraphQL 类型

Posted

技术标签:

【中文标题】构建 GraphQL 类型【英文标题】:Structuring GraphQL types 【发布时间】:2015-12-03 06:05:59 【问题描述】:

我在尝试扩展我的 API 以包含 GraphQL 端点时遇到了问题。我正在开发的应用程序是一种Messages 的论坛。消息可以包含 Message 类型的 cmets。如果消息是评论,则它的父级类型为Message。简化后,架构如下所示:

type Message 
  id: String
  content: String
  comments: [Message]
  parent: Message


type RootQuery 
  message(id: String): Message
  messages: [Message]

这个模式的问题是它允许这样的查询:


  messages 
    comments 
      parent 
        comments 
          parent 
            comments 
              parent 
                id
                content       
              
                   
          
           
      
    
  

请记住,我可能希望允许任意深度的 cmets 嵌套。在这种情况下,应该允许以下查询:


  messages 
    comments 
      comments 
        comments 
          id
          content
        
      
    
  

所以,我的问题是:我是否应该向不知道其父级的 API 引入一种新类型 - 注释?或者有没有其他方法可以限制这种不受欢迎的行为?

另外,评论类型的使用会禁止我在查询中使用fragment messageFields on Message 语法吗?也许现在是时候为模式引入接口了?

如果我引入类型 Comment 的解决方案建议(我没有尝试过):

interface Message 
  id: String
  content: String
  comments: [Message]


type DefaultMessage : Message 
  id: String
  content: String
  comments: [Comment]
  parent: Message


type Comment : Message 
  id: String
  content: String
  comments: [Message]


type RootQuery 
  message(id: String): Message
  messages: [Message]

【问题讨论】:

【参考方案1】:

以防万一其他人在这里想知道如何在 graphql-js 中执行递归类型,graphql-js 的代码中有一个有用的提示:

 * When two types need to refer to each other, or a type needs to refer to
 * itself in a field, you can use a function expression (aka a closure or a
 * thunk) to supply the fields lazily.
 *
 * Example:
 *
 *     var PersonType = new GraphQLObjectType(
 *       name: 'Person',
 *       fields: () => (
 *         name:  type: GraphQLString ,
 *         bestFriend:  type: PersonType ,
 *       )
 *     );
 *
 */

https://github.com/graphql/graphql-js/blob/master/src/type/definition.js#L274

【讨论】:

【参考方案2】:

如果消息是评论,则它有一个消息类型的父级。

看起来parent 字段应该在type Comment 下,而不是DefaultMessage。这仍然不会阻止parent - comments - parent 查询,但如果您出于 DDOS 原因担心这一点,那么即使使用 REST API,还有许多其他类型的请求难以计算,您应该有其他措施来检测这样的攻击。

递归节点

但是,您对嵌套 cmets 提出了一个非常有趣的问题。您怎么知道需要在查询中嵌套comment 多少次才能获得所有嵌套响应?我认为 GraphQL 目前无法指定递归对象。

我可能会绕过这个限制,从作为node 的最后一条评论开始逐个(或一次按 X 层)获取每个嵌套评论


  messages 
    comments 
      id
      content
    
  

紧随其后


  node(commendId) 
    comment 
      id
      content
    
  

【讨论】:

【参考方案3】:

我想你有 Comment 数据结构的 depth 属性,它应该非常有用,例如,当用户发布 cmets 时限制最大嵌套深度。

这样您的问题可以这样解决:在comments 属性的resolver 中,检查depth,如果depth 非法,则不返回任何内容,否则获取cmets 并返回。

【讨论】:

以上是关于构建 GraphQL 类型的主要内容,如果未能解决你的问题,请参考以下文章

构建时出现 TypeScript 错误,因为我导入提取的 .graphql 文件并且无法解析类型

Apollo Android (GraphQL) 自定义类型构建文件警告:“无法为最终字段赋值”

NestJS Graphql webpack 生产构建错误

GraphQL 项目结构

GraphQL.js - 时间戳标量类型?

如何使用 (where:) 构建 GraphQL 查询